1、自定义一个GifView
首先自定义一个GifView,用于显示Gif图片。GifView的代码参考自https://github.com/Cutta/GifView。
package cc.duduhuo.gifviewdemo.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Build;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
/**
* =======================================================
* 作者:liying - [email protected]
* 日期:2017/8/19 20:09
* 版本:1.0
* 描述:自定义显示Gif图片的View
* 备注:
* =======================================================
*/
public class GifView extends View {
private static final int DEFAULT_MOVIE_VIEW_DURATION = 1000;
private int mMovieResourceId;
private Movie mMovie;
private long mMovieStart;
private int mCurrentAnimationTime;
/**
* Position for drawing animation frames in the center of the view.
*/
private float mLeft;
private float mTop;
/**
* Scaling factor to fit the animation within view bounds.
*/
private float mScale;
/**
* Scaled movie frames width and height.
*/
private int mMeasuredMovieWidth;
private int mMeasuredMovieHeight;
private volatile boolean mPaused;
private boolean mVisible = true;
public GifView(Context context) {
this(context, null);
}
public GifView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GifView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
/*
* Starting from HONEYCOMB(Api Level:11) have to turn off HW acceleration to draw
* Movie on Canvas.
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
public void setGifResource(int movieResourceId) {
this.mMovieResourceId = movieResourceId;
mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
requestLayout();
}
public int getGifResource() {
return this.mMovieResourceId;
}
public void play() {
if (this.mPaused) {
this.mPaused = false;
/*
* Calculate new movie start time, so that it resumes from the same
* frame.
*/
mMovieStart = SystemClock.uptimeMillis() - mCurrentAnimationTime;
invalidate();
}
}
public void pause() {
if (!this.mPaused) {
this.mPaused = true;
invalidate();
}
}
public boolean isPaused() {
return this.mPaused;
}
public boolean isPlaying() {
return !this.mPaused;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mMovie != null) {
int movieWidth = mMovie.width();
int movieHeight = mMovie.height();
/*
* Calculate horizontal scaling
*/
float scaleH = 1f;
int measureModeWidth = MeasureSpec.getMode(widthMeasureSpec);
if (measureModeWidth != MeasureSpec.UNSPECIFIED) {
int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
if (movieWidth > maximumWidth) {
scaleH = (float) movieWidth / (float) maximumWidth;
}
}
/*
* calculate vertical scaling
*/
float scaleW = 1f;
int measureModeHeight = MeasureSpec.getMode(heightMeasureSpec);
if (measureModeHeight != MeasureSpec.UNSPECIFIED) {
int maximumHeight = MeasureSpec.getSize(heightMeasureSpec);
if (movieHeight > maximumHeight) {
scaleW = (float) movieHeight / (float) maximumHeight;
}
}
/*
* calculate overall scale
*/
mScale = 1f / Math.max(scaleH, scaleW);
mMeasuredMovieWidth = (int) (movieWidth * mScale);
mMeasuredMovieHeight = (int) (movieHeight * mScale);
setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);
} else {
/*
* No movie set, just set minimum available size.
*/
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
/* Calculate mLeft / mTop for drawing in center */
mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;
mTop = (getHeight() - mMeasuredMovieHeight) / 2f;
mVisible = getVisibility() == View.VISIBLE;
}
@Override
protected void onDraw(Canvas canvas) {
if (mMovie != null) {
if (!mPaused) {
updateAnimationTime();
drawMovieFrame(canvas);
invalidateView();
} else {
drawMovieFrame(canvas);
}
}
}
/**
* Invalidates view only if it is mVisible.
*
* {@link #postInvalidateOnAnimation()} is used for Jelly Bean and higher.
*/
@SuppressLint("NewApi")
private void invalidateView() {
if (mVisible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
postInvalidateOnAnimation();
} else {
invalidate();
}
}
}
/**
* Calculate current animation time
*/
private void updateAnimationTime() {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) {
mMovieStart = now;
}
int dur = mMovie.duration();
if (dur == 0) {
dur = DEFAULT_MOVIE_VIEW_DURATION;
}
mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
}
/**
* Draw current GIF frame
*/
private void drawMovieFrame(Canvas canvas) {
mMovie.setTime(mCurrentAnimationTime);
canvas.save();
canvas.scale(mScale, mScale);
mMovie.draw(canvas, mLeft / mScale, mTop / mScale);
canvas.restore();
}
@SuppressLint("NewApi")
@Override
public void onScreenStateChanged(int screenState) {
super.onScreenStateChanged(screenState);
mVisible = screenState == SCREEN_STATE_ON;
invalidateView();
}
/**
* get gif width
*
* @return width
*/
public int getGifWidth() {
return mMovie.width();
}
/**
* get gif height
*
* @return height
*/
public int getGifHeight() {
return mMovie.height();
}
@SuppressLint("NewApi")
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
}
重要的是要添加getGifWidth()
和getGifHeight()
两个方法,用于获取Gif图片的宽高。
(注:获取Gif图片的宽和高可以简单等价于获取对应Movie对象的宽和高。)
2、在布局文件中使用GifView
自定义的View可以在布局文件中直接使用。
3、缩放Gif图片,使之全屏显示
首先需要调用GifView
的setGifResource(int)
方法设置要显示的Gif图片。
GifView gifView = (GifView) findViewById(R.id.gifView);
gifView.setGifResource(R.drawable.gif1); // 设置显示的Gif图片
其次,调用GifView
的getGifWidth()
方法和getGifHeight()
方法得到Gif图片的宽和高。再获取到当前屏幕的宽和高,分别计算屏幕和Gif图片的宽度和高度比例。为了使图片的宽高比例保持不变,选择缩放比例较小的一边进行缩放。
int width = gifView.getGifWidth();
int height = gifView.getGifHeight();
int screenWidth = getScreenSize().width();
int screenHeight = getScreenSize().height();
if (width > 0 && height > 0) {
float wScale = (float) screenWidth / width;
float hScale = (float) screenHeight / height;
if (wScale < 1 || hScale < 1) {
// 如果图片的宽或高大于屏幕的宽或高,则图片会自动缩小至全屏
} else if (wScale <= hScale) {
// 宽度全屏
gifView.setScaleX(wScale);
gifView.setScaleY(wScale);
} else {
// 高度全屏
gifView.setScaleX(hScale);
gifView.setScaleY(hScale);
}
}
getScreenSize()
方法用于获取屏幕的宽度和高度。
/**
* 得到屏幕的尺寸
*
* @return 包含屏幕尺寸的Rect对象
*/
private Rect getScreenSize() {
Rect localRect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(localRect);
return localRect;
}
4、效果演示
宽度全屏 | 高度全屏 |
---|---|
5、代码下载
Demo代码下载:http://download.csdn.net/download/u012939909/9941754