Android开发之逐帧动画优化

    xiaoxiao2021-03-25  93

    Android上如果使用逐帧动画的话,可以很方便地使用AnimationDrawable,无论是先声明xml还是直接代码里设置,都是几分钟的事,但使用AnimationDrawable有一个致命的弱点,那就是需要一次性加载所有图片到内存,万一帧数多了或者每张图片都比较大,很容易就报out of memory的异常了,所以有必要进行优化。

    这里我们利用View.postDelayed方法延时替换图片,这样就能做到逐帧动画的效果了,然后在替换图片之前,强制回收ImageView当前bitmap就可以减少内存消耗了,废话少说,上代码。

    public class SceneAnimation { private ImageView mImageView; private int[] mFrameRess; private int[] mDurations; private int mDuration; private int mLastFrameNo; private long mBreakDelay = 0L; private int mLastPlayFrameNo = 0; private boolean isStop = true; public SceneAnimation(ImageView pImageView, int[] pFrameRess, int[] pDurations) { mImageView = pImageView; mFrameRess = pFrameRess; mDurations = pDurations; mLastFrameNo = pFrameRess.length - 1; // mImageView.setBackgroundResource(mFrameRess[0]); } public SceneAnimation(ImageView pImageView, int[] pFrameRess, int pDuration) { mImageView = pImageView; mFrameRess = pFrameRess; mDuration = pDuration; mLastFrameNo = pFrameRess.length - 1; // mImageView.setBackgroundResource(mFrameRess[0]); } public SceneAnimation(ImageView pImageView, int[] pFrameRess, int pDuration, long pBreakDelay) { mImageView = pImageView; mFrameRess = pFrameRess; mDuration = pDuration; mLastFrameNo = pFrameRess.length - 1; mBreakDelay = pBreakDelay; // mImageView.setBackgroundResource(mFrameRess[0]); } @SuppressWarnings("unused") private void play(final int pFrameNo) { mImageView.postDelayed(new Runnable() { public void run() { if (pFrameNo != mLastPlayFrameNo) { recycleImage(); mLastPlayFrameNo = pFrameNo; } mImageView.setBackgroundResource(mFrameRess[pFrameNo]); if (!isStop) { if (pFrameNo == mLastFrameNo) play(0); else play(pFrameNo + 1); } } }, mDurations[pFrameNo]); } private void playConstant(final int pFrameNo) { mImageView.postDelayed(new Runnable() { public void run() { if (pFrameNo != mLastPlayFrameNo) { recycleImage(); mLastPlayFrameNo = pFrameNo; } mImageView.setBackgroundResource(mFrameRess[pFrameNo]); if (!isStop) { if (pFrameNo == mLastFrameNo) playConstant(0); else playConstant(pFrameNo + 1); } } }, pFrameNo == mLastFrameNo && mBreakDelay > 0 ? mBreakDelay : mDuration); } public void stopPlay() { isStop = true; // recycleImage(); } public void playConstant() { isStop = false; playConstant(mLastPlayFrameNo); } private void recycleImage() { BitmapUtil.recycleImageView(mImageView); } public void playOnce(FinishCallback callback) { isStop = false; playOnce(callback, 0); } private void playOnce(FinishCallback callback, int frameNo) { mImageView.postDelayed(new Runnable() { public void run() { if (frameNo != 0) recycleImage(); mImageView.setBackgroundResource(mFrameRess[frameNo]); if (!isStop) { if (frameNo == mLastFrameNo) { isStop = true; if (callback != null) callback.onFinish(SceneAnimation.this); } else playOnce(callback, frameNo + 1); } } }, frameNo == mLastFrameNo && mBreakDelay > 0 ? mBreakDelay : mDuration); } public interface FinishCallback { public void onFinish(SceneAnimation sceneAnimation); } public boolean isRunning() { return !isStop; } }上面的类提供了两种方法,循环播放和只播放一次,stopPlay是停止当前动画,而mLastPlayFrameNo是当前图片是所有图片中的第几张,循环中当当前的frameNo不等于mLastPlayFrameNo时回收图片,这个相当重要,处理不当可能会报出使用回收后的bitmap的异常,因为有可能用户一开始ImageView设置的src就是第0张,又或者用户停止动画后又想重新播放,那么就会发生上面的情况。

    好了,讲述完这个类,看一下如何使用吧,很简单。

    SceneAnimation waitAnim = new SceneAnimation(waitImageView, waitResIds, 100); // 指定绑定的ImageView和图片资源数组以及每张图片的延时 waitAnim.playConstant(); // 循环播放 waitAnim.stopPlay(); // 停止播放 逐帧动画优化到这里结束了,后期我们或许可以继续优化,就是防止一个图片帧太大,加载时间过长,我们可以缓存多张,而不是现在的只缓存一张。

    转载请注明原文地址: https://ju.6miu.com/read-20698.html

    最新回复(0)