Android自定义控件播放GIF动画
转自:http://www.ilovn.com/topic/android-custom-control-play-gif-animation/
谢谢原作者
============================================================================================
Android播放Gif的方案有几种,比如使用webview、解帧启用线程播放等,这里使用的是Movie这个类来完成。
我们查看SDK目录下的例子,google给了我们一个例子,具体位置在SDK_HOME/samples/android-17/ApiDemos/src/com/example/android/apis/graphics/BitmapDecode.java ,这里有如何去使用。这篇文章的目的是要做一个通用的GIF播放的自定义控件,这里会介绍如果自己构建这个简单的控件,然后一步一步进行一些必要的扩展,实现代码使用和xml配置使用的过程。
需要自定义控件,那么我们就需要创建一个继承自View的类GifView,并且重写默认的几个构造方法
package com.ilovn.app.test.dynamicface; import android.content.Context; import android.graphics.Movie; import android.view.View; public class GifView extends View { private Movie mMovie; private long movieStart; public GifView(Context context) { super (context); initializeView(); } public GifView(Context context, AttributeSet attrs, int defStyle) { super (context, attrs, defStyle); initializeView(); } public GifView(Context context, AttributeSet attrs) { super (context, attrs); initializeView(); } /** * 初始化设置 */ private void initializeView() { //TODO 在这里做一些初始化工作,这里代码先不写,我们需要扩展 } } |
我们需要使用OnDraw(Canvas)还绘制View,但在此之前,我们必须让Movie对象知道怎么播放,需要播放哪部分,要做到这一点,我们需要一个独立的变量movieStart长型的,这里我们分配SystemClock.uptimeMillis()值。通过获取整个GIF的播放时间,计算应该播放gif的哪一帧,通过mMovie.setTime()。
@Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.TRANSPARENT); super .onDraw(canvas); long now = android.os.SystemClock.uptimeMillis(); if (movieStart == 0 ) { movieStart = now; } int d = mMovie.duration(); if (d <= 0 ) { d = 1000 ; } if (mMovie != null ) { int relTime = ( int ) ((now - movieStart) % d); mMovie.setTime(relTime); mMovie.draw(canvas, (getWidth() - mMovie.width()) / 2 , (getHeight() - mMovie.height()) / 2 ); this .invalidate(); } } |
到这里还不够,我们还需要做的是告诉GifView应该显示的资源来源是什么,根据我们平时使用的提供的View可知,我们应该至少提供java代码指定资源和xml配置资源两种形式。
首先是使用xml配置的资源,我们需要自定义一个在xml中参数来指定资源,我们在values/attrs.xml加入
< declare-styleable name = "GIFView" > < attr name = "src" format = "integer" /> </ declare-styleable > |
使用了自定义的参数后,我们在xml中配置的时候,需要指定一个前缀,类似:
xmlns:gif="http://schemas.android.com/apk/res/<your app packagename 如:com.ilovn.app.test.dynamicface>"
然后在GifView的配置中这样使用:
< com.ilovn.app.test.dynamicface.GifView android:id = "@+id/imageview_smile" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_below = "@+id/gridview" android:layout_gravity = "center" android:scaleType = "center" gif:src = "@drawable/bb45" /> |
接下来在GifView里面,初始化的时候要处理:
private int gifResource; /** * 初始化设置 */ private void initializeView() { if (gifResource != 0 ) { InputStream is = getContext().getResources().openRawResource( gifResource); byte [] array = streamToBytes(is); mMovie = Movie.decodeByteArray(array, 0 , array.length); movieStart = 0 ; this .invalidate(); } } |
同时提供java代码中的调用设置资源ID的方法:
/** * 设置GIF资源ID * * @param id */ public void setGIFResource( int id) { this .gifResource = id; initializeView(); } |
为了兼顾两种模式,我们需要提供是否使用xml配置还是java配置:
private boolean setAttrs(AttributeSet attrs) { boolean f = false ; if (attrs != null ) { TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.GIFView); int taCount = ta.length(); for ( int i = 0 ; i < taCount; i++) { if (R.styleable.GIFView_src == ta.getIndex(i)) { int id = ta.getResourceId(R.styleable.GIFView_src, 0 ); if (id != 0 ) { setGIFResource(id); f = true ; } } } ta.recycle(); } return f; } |
在初始化的时候进行判断:
public GifView(Context context) { super (context); initializeView(); } public GifView(Context context, AttributeSet attrs, int defStyle) { super (context, attrs, defStyle); if (!setAttrs(attrs)) { initializeView(); } } public GifView(Context context, AttributeSet attrs) { super (context, attrs); if (!setAttrs(attrs)) { initializeView(); } } |
这样就提供了较为方便的设定gif资源,但是,实际在java代码中,我们可能更多的是指定其他资源类型,而不是资源ID,那么我们应该提供更多的方法支持:
/** * 设置GIF资源数据 * * @param bytes */ public void setGIFResource( byte [] bytes) { if (bytes == null || bytes.length <= 0 ) { return ; } mMovie = Movie.decodeByteArray(bytes, 0 , bytes.length); movieStart = 0 ; this .invalidate(); } /** * 设置GIF资源数据 * * @param is */ public void setGIFResource(InputStream is) { if (is == null ) { return ; } byte [] array = streamToBytes(is); mMovie = Movie.decodeByteArray(array, 0 , array.length); movieStart = 0 ; this .invalidate(); } /** * 设置GIF资源图片 * * @param bitmap */ public void setGIFResource(Bitmap bitmap) { if (bitmap == null ) { return ; } ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(CompressFormat.PNG, 0 /* ignored for PNG */ , bos); byte [] bitmapdata = bos.toByteArray(); mMovie = Movie.decodeByteArray(bitmapdata, 0 , bitmapdata.length); movieStart = 0 ; this .invalidate(); } |
这样就算是完成了一个比较完整的GifView了。附上GivView 的源码。