网上有很多关于怎么实现android播放GIF的帖子。但是本人发现,其中多多少少都有些不如人意的地方。因此,花了几天时间,重写了ImageView以实现GIF图片的播放。在此小结一下,也希望可以给后来者一点参考。
大致我们会在网上搜到下面四种解决方法:
【方案一】用外部工具拆分GIF
【方案二】用Android开源项目GifView包
【方案三】手动解码GIF
【方案四】用系统自带的类Movie
本文采用方案四,继承ImageView实现GIF动画播放,支持ImageView的命名空间属性设置,支持ImageView通用接口。
项目源码下载地址:
http://download.csdn.net/detail/yarkey09/6499717
【什么是GIF】
GIF,就我的理解,就是很多张位图图片的集合,然后使用了某种编码方式,使得它可以体积很小但是又够清晰。由于体积小,不依赖特别的平台,所以GIF很流行。
好吧,知道的就这么多,各位看官想了解清楚的话还是请自行百度吧。不过了解了大概概念,我们就可以知道,其实让GIF播放,实际就是显示多张图片而已。
【方案一】用外部工具拆分GIF
大概情况是这样:
1,首先我们得有一张GIF (提示:选择赏心悦目的动画,可以提高学习兴趣哦^_^)
2,然后使用工具,千刀万剐将GIF分成多张图片 => pic0.png,pic1.png,pic2.png,pic3.png,pic4.png,pic5.png
3,接着编写android xml资源文件放在drawable目录下,说明各个帧图片以及时间duration
4,然后代码里面使用AnimationDrawable类即可实现
四张图片按照xml定义的时间,一张张切换,看起来就是动画了!
动画资源文件格式是这样:(drawable/anim_gif.xml)
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:duration="150" android:drawable="@drawable/pic0" />
<item android:duration="150" android:drawable="@drawable/pic1" />
<item android:duration="150" android:drawable="@drawable/pic2" />
<item android:duration="150" android:drawable="@drawable/pic3" />
<item android:duration="150" android:drawable="@drawable/pic4" />
<item android:duration="150" android:drawable="@drawable/pic5" />
</animation-list>
布局文件可以是这样:
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/anim_gif"
android:id="@+id/imgGif"></ImageView>
代码是这样:
ImageView imageView = (ImageView) findViewById(R.id.imgGif);
Object ob = imageView.getBackground();
AnimationDrawable anim = (AnimationDrawable) ob;
anim.start();
如果我们需要在界面上显示一个简单且固定的动画,单纯用于点缀画面,增强动感,这种方法比较方便。当然,这种方法在某些场合下显得很不灵活,不能满足要求效果,那么请继续参考后面的方法吧。
【方案二】用Android开源项目GifView包
我们同样可以在网上搜到这个开源项目的相关应用。有了这个包,我们要让GIF播放这个事情就变得非常轻松。看看它那强大的接口就知道了!使用GifView几乎就跟ImageView是一样的。方便!开源项目确实有很多代码都有非常好的学习价值,表示有空应该好好拜读一番!
// 从xml中得到GifView的句柄
gif1 = (GifView) findViewById(R.id.gif1);
// 设置Gif图片源
gif1.setGifImage(R.drawable.gif1);
// 添加监听器
gif1.setOnClickListener(this);
// 设置显示的大小,拉伸或者压缩
gif1.setShowDimension(300, 300);
// 设置加载方式:先加载后显示、边加载边显示、只显示第一帧再显示
gif1.setGifImageType(GifImageType.COVER);
参考帖子:介绍一个Android开源项目:GifView——Android显示GIF动画
【方案三】手动解码GIF
用java来解码,很多人会觉得效率比较低。但是我们目的是学习,完全可以尝试一下!当然,也可以用native代码完成解码,在java用JNI调用。
将GIF文件解码后,我们可以得到所有想要的信息。比如Gif版本GIF87a, GIF89a等等,关键是我们可以得到几张Bitmap图片,还有各张图片的显示延续时间。其实,这里解码工作也就大致等同于上面的方案一。不同的是,我们的app可以直接播放GIF,而不需要外部的工具!
有了各帧图片以及显示延续时间,我们便可以开始了!新建一个线程用于计时,时间一到就刷新View切换图片。这就是GIF了!
注意一下在非主线程让View刷新,应该调用postInvalidate() 而不是invalidate()。
下面参考帖子附有解码源程序,然后按照参考文档来阅读,很快可以看明白^_^
参考帖子: Android 解码播放GIF图像
参考文档: GIF文件格式
【方案四】用系统自带的类Movie
接下来说说具体要讲的基于Movie的实现方法吧!
使用Movie类播放GIF很简单。但是我们的目的是,继承ImageView,保留它显示图片的基本功能,尽量使得接口函数能够通用简便。这样,原先使用ImageView的项目代码只要经过少量的修改,即可支持GIF动画。根据要求,我们至少要重载下面四个通用接口以支持GIF动画:
public void setImageResource( int resID )
public void setImageURI( Uri uri )
public void setScaleType( ScaleType scaleType )
public void setPadding( int left, int top, int right, int bottom )
说明一下:
// 我们设置了图片,那么跟ImageView一样显示出图片
setImageResource( R.drawable.pngtest );
// 我们这次设置了GIF动画,那么应该显示动画
setImageResource( R.drawable.giftest );
// 支持SD卡中的GIF动画
setImageURI( Uri.parse( "file://" + Environment.getExternalStorageDirectory().getPath() + "/sdcard_giftest.gif" );
为了实现以上要求,其中遇到很多问题,我们慢慢说吧。
-1- Movie 是啥东西
android.graphics.Movie 在SDK文档中没有说明,翻看源代码,发现它只是一个java壳,实际上直接调用native代码。这样导致我们没能快速学习掌握它的用法。
不过幸亏有APIDemo!这真的是一个好东西!打开其中BitmapDecode我们可以发现代码中就用了Movie类!
直接安装APIDemo到手机中,运行... 发现旗子飘动起来了!
它的源代码简单清晰,大概是这样。Movie对象管理着时间轴上对应的GIF各帧图片,我们通过传入时间,便可以取出对应的帧,然后再用draw()方法,将当前的帧画到画布canvas上面。如果我们的View不停的刷新,时间不停地跑,Movie的帧就不停的切换,那么画出来的View就动起来了!
-2- Copy APIDemo 源码
那么好了,按照它的代码,我们可以很快copy一份出来,然后编译安装到手机,我们想GIF似乎就这样完成了。关键代码如下。
private static class MovieGifView extends View {
private Movie mMovie;
private long mMovieStart;
public MovieGifView(Context context) {
super(context);
java.io.InputStream is;
is = context.getResources().openRawResource(R.drawable.animated_gif);
mMovie = Movie.decodeStream(is);
}
@Override
protected void onDraw(Canvas canvas) {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) { // first time
mMovieStart = now;
}
if (mMovie != null) {
int dur = mMovie.duration();
if (dur == 0) {
dur = 1000;
}
int relTime = (int) ((now - mMovieStart) % dur);
mMovie.setTime(relTime);
mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight() - mMovie.height());
invalidate();
}
}
}
但是结果却是那么不如人意,自己写的app在一部平板(android 4.3)上运行时,GIF没有动起来,美女并没有向我眨眼!
我第一反应便是拿去我的屌丝神机I589(I5830电信版 android 2.3) 上试试。结果反而动起来了!这么神马回事?!
难道是android 4.3 版本太新,Movie方法不支持?后来我又找到了一部android 4.1 的手机,安装发现,GIF同样没有动!
奇怪!头疼!
-3- hardwareAccelerated 惹的祸
为什么APIDemo的代码可以,我的代码直接copy,却不行了?我翻看了很久代码,最后找到了唯一不同点,在这里 -> AndroidManifest.xml
<activity android:hardwareAccelerated="false"
android:name=".graphics.BitmapDecode" android:label="Graphics/BitmapDecode">
<intent-filter>
<action android:name&#