前言
逐帧动画 (Frame By Frame) 是 Android 系统提供的一种常见的动画形式,通过播放一组连续的图片资源形成动画。当我们想用一组连续的图片播放动画时,首先想到的就是使用系统提供的逐帧动画方式。接下来,我们将简单说明如何使用逐帧动画,以及分析逐帧动画存在的优缺点,最后给出我们的解决方案。
逐帧动画
第一步,将我们所需要的动画素材资源放置在 res/drawable 目录下,切记不要因为是动画所以就错误的将素材资源放置在 res/anim 目录下。
第二步,在 res/anim 目录下新建 drawable 文件 loading.xml ,如下
animation-list 为 drawable 文件的根标签,android:oneshot 设置动画是否只播放一次,子标签 item 具体定义每一帧的动画,android:drawable 定义这一帧动画所使用的资源,android:duration 设置动画的持续时间。
第三步,给想要显示动画的 ImageView 设置资源动画,然后开启动画
我们能看到,逐帧动画使用起来是如此的简单方便,所以当我们想要通过一组图片素材来实现动画的时候首选的就是以上的方案。但是我们却忽略了一个情况,当图片素材很多并且每张图片都很大的情况下,使用以上的方法手机会出现 OOM 以及卡顿问题,这是帧动画的一个比较明显的缺点。
为什么帧动画会出现 OOM 以及卡顿?
我们知道,在第三步给 ImageView 设置图片资源的时候,因为 loading.xml 文件中定义了一系列的图片素材,系统会按照每个定义的顺序把所有的图片都读取到内存中,而系统读取图片的方式是 Bitmap 位图形式,所以就导致了 OOM 的发生。
解决方案
既然一次性读取所有的图片资源会导致内存溢出,那么我们能想到的解决方法就是按照动画的顺序,每次只读取一帧动画资源,读取完毕再显示出来,如果图片过大,我们还需要对图片进行压缩处理。
技术实现
总体思路是这样的,我们在子线程里读取图片资源(包括图片过大,对图片进行处理),读取完毕后通过主线程的 Handler 将在子线程的数据(主要是 Bitmap)发送到主线程中,然后再把 Bitmp 绘制显示出来,每隔一段时间不断读取,然后依次显示出来,这样视觉上就有了动画的效果。实现代码如下
public class AnimationView extends View implements Handler.Callback {
public static final int DEFAULT_ANIM_TIME = 100;
public static final int PROCESS_DATA = 1;
public static final int PROCESS_ANIM_FINISH = 1 << 1;
public static final int PROCESS_DELAY = 1 << 2;
public AnimData mCurAnimData;
public int mCurAnimPos;
public boolean mIsRepeat;
public int mAnimTime;
private Handler mHandler ;
private ProcessAnimThread mProcessThread;
private Bitmap mCurShowBmp;
private List mAnimDataList = new ArrayList<>();
public AnimationView(Context context) {
this(context,null);
}
public AnimationView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public AnimationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mHandler = new H