前言
Gif动画图在Android开发中很常见,但是Android自带的ImageView控件并不支持Gif动画效果,直接将Gif图设置到ImageView只会展示其中的一帧静态图。本文将探讨常用的展示Gif动态图的方法。
Movie播放
android.graphics.Movie对象能够将gif图播放出来,所以可以自定义GifImageView控件并在它的onDraw方法中使用Movie将gif每个时间点的帧画到Canvas上,之后在不停的更新GifImageView,每次画不同时间点的帧到Canvas上就实现了动态播放效果。
必须关闭所在的Activity的硬件加速功能,使用android:hardwareAccelerated=”false”配置Activity,否则无法看到动态播放效果。
实现代码如下,首先是自定义的GifImageView:
public class GifImageView extends AppCompatImageView {
private static final String TAG = "GifImageView";
private int mGifUrl = 0; // Gif图所在的路径
private Movie mMovie; // 播放Gif图的movie独享
private int duration = 0; // Gif图持续时间
private long start = -1; // Gif图开始播放时间,初始化为-1
public GifImageView(Context context) {
this(context, null);
}
public GifImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public GifImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (attrs != null) {
// 通过自定义属性的方式获取gif图的路径
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GifImageView);
mGifUrl = typedArray.getResourceId(R.styleable.GifImageView_gif_url, 0);
typedArray.recycle();
}
if (mGifUrl != 0) {
String imageType = null;
try {
// 根据文件内容前4个字节判断文件是否是Gif图
imageType = BitmapUtils.getImageType(context.getResources().openRawResource(mGifUrl));
if ("gif".equalsIgnoreCase(imageType)) {
// 将Gif图加载到内存当中
byte[] array = streamToBytes(context.getResources().openRawResource(mGifUrl));
Log.d(TAG, "array.length = " + array.length);
// 将Gif图加载到movie对象里
mMovie = Movie.decodeByteArray(array, 0, array.length);
// 获取动画效果时长
duration = mMovie.duration();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int len;
try {
while ((len = is.read(buffer)) >= 0) {
os.write(buffer, 0, len);
}
} catch (java.io.IOException e) {
}
return os.toByteArray();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas);
if (mMovie != null) {
if (start == -1) {
// 刚开始播放,初始化播放开始时间
start = SystemClock.uptimeMillis();
}
// 根据当前时间和播放开始时间确定目前需要展示的帧
int time = (int) ((SystemClock.uptimeMillis() - start) % duration);
Log.d(TAG, "time = " + time + ", duration = " + duration);
// movie指向展示的帧
mMovie.setTime(time);
// 画出当前帧到GifImageView视图上
mMovie.draw(canvas, 0, 0);
// 持续更新GifImageView视图,实现动态展示效果
postInvalidateDelayed(10);
}
}
}
注意放置GifImageView的Activity关闭硬件加速功能。
<activity
android:name=".MainActivity"
android:hardwareAccelerated="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
WebView播放
WebView模仿的是浏览器功能,一般的浏览器都可以展示Gif动图,所以可以直接将Gif的url加载到WebView中展示。自定义的GifWebView如下所示:
gifWebView = (GifWebView) findViewById(R.id.gif_web_view);
gifWebView.loadUrl("file:///android_asset/hongbao.gif");
实现简单,而且不需要禁用硬件加速功能。
Glide播放
Glide框架提供了完美的Gif动图展示功能,它将Gif图包装成了GifDrawable然后设置到ImageView中,通常在真实项目中也使用这种方式。具体的实现原理还需要阅读Glide的源码,这里暂不深入介绍。
总结
Movie播放Gif实现比较原始,而且代码逻辑也比较复杂,选择的自动刷新时间也会影响Gif图的展示效果。WebView实现非常简单,但是很难控制Gif图与WebView大小一致,需要做缩放处理。Glide实现简单易用,但是需要整体依赖Glide库,不过对于大部分Android应用都需要图片加载框架,依赖Glide似乎不是个大问题。