android 显示GIF动画
2016-09-07 12:01
393 查看
因为公司项目的需要,需要从后台获取gif动画,然后展示。Android本身并没有提供相关的直接显示gif动画的GifView之类的,通常使用webview显示,然后去搜索了下,发现有另外的实现思路,然后就借鉴了下。在这里总结下。另外,自己也动手写了一个gif加载框架,GifLoader,后面会开一篇讲解gifloader使用以及框架思路,已上传至jcenter 有需要的同学点这里
显示gif主要用到了android.graphics.Movie这个类
可以看到,movie提供了三个decode方法,分别从byte[],file,InputStream中获取数据,并转换为movie对象,实际上movie对象中的数据即是要展示的gif数据,看看每个方法的具体含义
那怎么使用呢?我们上GifView源码
代码还是比较简洁清晰的
继承自ImageView是因为加载gif是一个比较耗时的工作(从网上下载),这个时候可以设置一张过渡图片,用户体验较好。
onMeasure()的作用是测量view大小,当设置wrap_content时,取movieSize和viewSize较小值
onLayout里面的代码是为了计算mScale,即gif内容部分需要放大或者缩小的比例,如果不计算则会出现,内容和控件尺寸不匹配的情况,ondraw则根据动画时长,和当前时间,计算出当前显示的帧,类似于动画差值器,然后去绘制当前显示的画面。
使用也比较简单,直接调用几个set方法,将movie,或者gif资源传给gifview即可。
最后上效果
效果还是挺流畅的,喜欢的同学可以拿去
另外也开源了gifloader加载网络gif gifloader框架 需要的同学可以在项目中使用,后面会专门介绍gifloader的使用以及框架的解析
欢迎拍砖!
显示gif主要用到了android.graphics.Movie这个类
可以看到,movie提供了三个decode方法,分别从byte[],file,InputStream中获取数据,并转换为movie对象,实际上movie对象中的数据即是要展示的gif数据,看看每个方法的具体含义
draw(Canvas canvas, float x, float y) //canvas 绘制的画布,x,绘制左边起始位置,y,绘制的头起始部位置 draw(Canvas canvas, float x, float y, Paint paint) //同上,多了一个paint,自定义画笔 duration() //gif的动画时长 height() //gif高度,这里的高度是指,gif图片内容本身的高度,非view的高度 isOpaque() //是否透明 setTime(int relativeMilliseconds) //设置当前的时间,可以控制当前显示哪一帧 width() // 同height()
那怎么使用呢?我们上GifView源码
import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Movie; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.ImageView; public class GifView extends ImageView { private static final String TAG = "ImageView"; private static final int DEFAULT_MOVIEW_DURATION = 2000; 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; private int mMeasureWidth; private int mMeasureHeight; /** * Scaling factor to fit the animation within view bounds. */ private float mScale = 1; 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); setViewAttributes(); } @SuppressLint("NewApi") public void setViewAttributes() { /** * 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); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mMovie != null) { if (!mPaused) { updateAnimationTime(); drawMovieFrame(canvas); invalidateView(); } else { drawMovieFrame(canvas); } } } /** * Draw current GIF frame */ private void drawMovieFrame(Canvas canvas) { mMovie.setTime(mCurrentAnimationTime); canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.scale(mScale, mScale); mMovie.draw(canvas, mLeft / mScale, mTop / mScale); canvas.restore(); } /** * 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_MOVIEW_DURATION; } mCurrentAnimationTime = (int) ((now - mMovieStart) % dur); } /** * Invalidates view only if it is visible. * <br> * {@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 4000 (); } else { invalidate(); } } } @SuppressLint("NewApi") @Override public void onScreenStateChanged(int screenState) { super.onScreenStateChanged(screenState); mVisible = screenState == SCREEN_STATE_ON; invalidateView(); } @SuppressLint("NewApi") @Override protected void onVisibilityChanged( 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(); } public void setMovieResource(int movieResId) { this.mMovieResourceId = movieResId; mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId)); requestLayout(); } public void setMovie(Movie movie) { this.mMovie = movie; setScale(); invalidate(); } private void setScale() { if (mMovie != null) { float scaleW = (float) getWidth() / mMovie.width(); float scaleH = (float) getHeight() / mMovie.height(); mScale = Math.min(scaleH, scaleW); } } public void setMovieTime(int time) { mCurrentAnimationTime = time; invalidate(); } public void setPaused(boolean paused) { this.mPaused = paused; /** * Calculate new movie start time, so that it resumes from the same * frame. */ if (!paused) { mMovieStart = android.os.SystemClock.uptimeMillis() - mCurrentAnimationTime; } invalidate(); } public boolean isPaused() { return this.mPaused; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed) { if (mMovie != null) { float scaleW = (float) getWidth() / mMovie.width(); float scaleH = (float) getHeight() / mMovie.height(); mScale = Math.min(scaleH, scaleW); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mMovie != null) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.AT_MOST) { Log.e(TAG, "---moviewidth before" + mMovie.width()); int movieWidth = mMovie.width(); mMeasureWidth = Math.min(movieWidth, widthSize); Log.e(TAG, "---moviewidth after" + mMovie.width()); } else { mMeasureWidth = widthSize; } if (heightMode == MeasureSpec.AT_MOST) { Log.e(TAG, "---height " + mMovie.height()); int movieHieght = mMovie.height(); mMeasureHeight = Math.min(movieHieght, heightSize); } else { mMeasureHeight = heightSize; } Log.e(TAG, "--wi : " + mMeasureWidth + " h : " + mMeasureHeight); setMeasuredDimension(mMeasureWidth, mMeasureHeight); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
代码还是比较简洁清晰的
继承自ImageView是因为加载gif是一个比较耗时的工作(从网上下载),这个时候可以设置一张过渡图片,用户体验较好。
onMeasure()的作用是测量view大小,当设置wrap_content时,取movieSize和viewSize较小值
onLayout里面的代码是为了计算mScale,即gif内容部分需要放大或者缩小的比例,如果不计算则会出现,内容和控件尺寸不匹配的情况,ondraw则根据动画时长,和当前时间,计算出当前显示的帧,类似于动画差值器,然后去绘制当前显示的画面。
使用也比较简单,直接调用几个set方法,将movie,或者gif资源传给gifview即可。
最后上效果
效果还是挺流畅的,喜欢的同学可以拿去
另外也开源了gifloader加载网络gif gifloader框架 需要的同学可以在项目中使用,后面会专门介绍gifloader的使用以及框架的解析
欢迎拍砖!
相关文章推荐
- Android中用GifView显示Gif动画及Gifview简介
- Android UI开发第十九篇——介绍一个开源的gifview——Android显示GIF动画
- Android显示GIF动画
- Android显示GIF动画完整示例(一)
- Android显示GIF动画
- Android UI开发第十九篇——介绍一个开源的gifview——Android显示GIF动画
- 介绍一个Android开源项目:GifView——Android显示GIF动画
- 在Android中显示GIF动画
- 在Android中显示GIF动画
- GifView——Android显示GIF动画
- Android显示GIF动画完整示例(二)
- Android显示GIF动画完整示例(二)
- GifView——Android显示GIF动画
- Android UI开发第十九篇——介绍一个开源的gifview——Android显示GIF动画
- Android显示GIF动画问题
- Android显示GIF动画
- Android显示GIF动画 GifView
- 介绍两个Android开源项目:Android显示GIF动画
- Android显示GIF动画GifView
- Android开发全程记录(十四)——Android显示gif动画的方法