效果图如上。
大家好,最近项目中需要用到gif动画显示,所以就着手研究了下gif的动画显示。
原理主要就是使用Movie的功能,先来看下这个类的主要几个方法:
public class Movie {
public native int width();
public native int height();
public native boolean isOpaque();
public native int duration();
}
Movie有长、宽以及时长,感觉像是表达电影的一个类。不过gif的原理类似电影,故也能应用加载gif,然后播放gif。为了使用方便,就直接吧这个功能做成了一个自定义drawable-GifDrawable。
GifDrawable即可以加载res资源文件,也可以从加载输入流,所以使用方便。因为比较简单,故直接上源码不多说了。
package com.hai.dialdemo;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Movie;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RawRes;
import androidx.annotation.UiThread;
import java.io.InputStream;
/**
* load a gif into drawable,compatible with < api28,after >= api28 ,AnimatedImageDrawable is recommended.
*/
public class GifDrawable extends Drawable {
private static final String TAG = "GifDrawable";
private static final int DEFAULT_MOVIE_DURATION = 1000;
private static final int GIF_TYPE_ID = 0X10;
private static final int GIF_TYPE_IS = 0X11;
private Handler mHandler = new Handler(Looper.getMainLooper());
private Listener mListener;
private Movie mMovie;
private int mGifType;
private Resources mResources;
private InputStream mIs;
private int mResId;
private long mTimeStart;
private long mTimeCurrent;
public GifDrawable() {
}
public GifDrawable(Resources res, int resId) {
this.mResources = res;
this.mResId = resId;
mGifType = GIF_TYPE_ID;
init();
}
public GifDrawable(@NonNull InputStream is) {
this.mIs = is;
mGifType = GIF_TYPE_IS;
init();
}
public InputStream getIs() {
return mIs;
}
public void setIs(InputStream mIs) {
this.mIs = mIs;
mGifType = GIF_TYPE_IS;
init();
}
public int getResId() {
return mResId;
}
public void setResId(Resources res, int resId) {
this.mResources = res;
this.mResId = resId;
mGifType = GIF_TYPE_ID;
init();
}
private void init() {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
if (mGifType == GIF_TYPE_ID) {
if (mResources != null && mResId > 0) {
mMovie = Movie.decodeStream(mResources.openRawResource(mResId));
mTimeStart = 0;
gifLoaded();
}
} else if (mGifType == GIF_TYPE_IS) {
if (mIs != null) {
mMovie = Movie.decodeStream(mIs);
mTimeStart = 0;
gifLoaded();
}
} else Log.e(TAG, "gif load failed with mGifType:" + mGifType);
}
});
}
private void gifLoaded() {
mHandler.post(() -> {
if (mListener != null) {
mListener.onGifLoaded(this);
invalidateSelf();
}
});
}
@Override
public void draw(@NonNull Canvas canvas) {
if (mMovie == null) return;
// Log.d(TAG, "draw() called with: canvas = [" + canvas + "]");
updateTimeCurrent();
float sx = 1f * getBounds().width() / mMovie.width();
float sy = 1f * getBounds().height() / mMovie.height();
Log.d(TAG, "draw() called with: sx = [" + sx + "]sy = [" + sy + "]");
canvas.save();
//scale the gif draw to fill bound of this.
canvas.scale(sx, sy);
mMovie.setTime((int) mTimeCurrent);
mMovie.draw(canvas, 0, 0);
canvas.restore();
if (isVisible())
invalidateSelf();
}
private void updateTimeCurrent() {
if (mTimeStart == 0) mTimeStart = SystemClock.uptimeMillis();
int movieDuration = mMovie.duration();
if (movieDuration <= 0) movieDuration = DEFAULT_MOVIE_DURATION;
mTimeCurrent = (SystemClock.uptimeMillis() - mTimeStart) % movieDuration;
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
public Listener getListener() {
return mListener;
}
public void setListener(Listener mListener) {
this.mListener = mListener;
}
public interface Listener {
@UiThread
void onGifLoaded(Drawable drawable);
}
}
使用demo
ImageView iv = findViewById(R.id.iv);
iv.setImageDrawable(new GifDrawable(getResources(),R.drawable.f3234));
后面发现glide自带gif显示功能,功能更强大方便,所以还是推荐大家用glide显示gif吧。