针对多个帧动画相互切换且帧数较多情况的另一种实现方式,优化方案,流畅不卡顿

最近有一个项目,有很多组帧动画,每一组大概60帧左右,再不同的状态下回调不同的接口,动态的切换到不同的帧动画,但是不管每一帧只有10K左右,使用软引用,提前加载帧动画,这些操作都试过了,但是再切换动画的时候还是有不时的有卡顿。由于图片过多,效果异常卡顿,所以在这篇文章中就说一下帧动画的优化问题。 首先还是先来看一下android原生的帧动画的实现,代码如下:
(1)帧动画的资源文件 放入res/drawable下

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

(2)布局文件

    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.fareast.dealframeanimation.MainActivity">
    

(3)Java代码实现开启帧动画//animationImg为ImageView的控件

AnimationDrawable animationDrawable= (AnimationDrawable) animationImg.getDrawable();
animationDrawable.start();

以上是android原生实现帧动画的,如果图片比较少可以这样使用,但是图片过多的话在低端机上就会造成卡顿,甚至会出现OOM异常,为了解决这个问题,可以把帧动画的图片一张张的分开,读到哪一张图就加载哪一张图,加载后把上一张图片释放。具体代码如下:(1)帧动画的资源文件 放入res/values/arrays.xml文件中 如果没有arrays.xml文件就新建一个


    
        @drawable/animation2
        @drawable/animation3
        @drawable/animation4
        @drawable/animation5
        @drawable/animation6
        @drawable/animation7
        @drawable/animation8
        @drawable/animation9
        @drawable/animation10
        @drawable/animation11
        @drawable/animation12
        @drawable/animation13
        @drawable/animation14
        @drawable/animation15
        @drawable/animation16
        @drawable/animation17
        @drawable/animation18
        @drawable/animation19
        @drawable/animation20
        @drawable/animation21
        @drawable/animation22
        @drawable/animation23
        @drawable/animation24
        @drawable/animation25
        @drawable/animation26
        @drawable/animation27
        @drawable/animation28
        @drawable/animation29
        @drawable/animation30
        @drawable/animation31
        @drawable/animation32
        @drawable/animation33
        @drawable/animation34
        @drawable/animation35
        @drawable/animation36
        @drawable/animation37
        @drawable/animation38
        @drawable/animation39
        @drawable/animation40
        @drawable/animation41
        @drawable/animation42
        @drawable/animation43
        @drawable/animation44
        @drawable/animation45
        @drawable/animation46
        @drawable/animation47
        @drawable/animation48
        @drawable/animation49
        @drawable/animation50
        @drawable/animation51
        @drawable/animation52
        @drawable/animation53
        @drawable/animation54
        @drawable/animation55
        @drawable/animation56
        @drawable/animation57
        @drawable/animation58
        @drawable/animation59
        @drawable/animation60
        @drawable/animation61
        @drawable/animation62
        @drawable/animation63
        @drawable/animation64
        @drawable/animation65
        @drawable/animation66
        @drawable/animation67
        @drawable/animation68
        @drawable/animation69
        @drawable/animation70
        @drawable/animation71
    

(2)布局文件同上面的原生帧动画的布局文件一样,这里不再多做声明。创建一个类继承自Application 在这个类中获取全局上下文,构建单例,别忘记在AndroidManifest.xml中注册Application否则无效,代码如下:


    private static Context mContext;

    @Override public void onCreate() {
        super.onCreate();
//        Fresco.initialize(this);//Fresco初始化
        MyApplication.mContext = getApplicationContext();
    }

    public static Context getAppContext() {
        return MyApplication.mContext;
    }

}(3)功能类 在此处理帧动画的加载的问题 播放 暂停帧动画的功能,具体情况如下代码:public class AnimationsContainer {
    public int FPS = 40;  // 每秒播放帧数,fps = 1/t,t-动画两帧时间间隔
    private int resId = R.array.loading_anim; //图片资源
    private Context mContext = MyApplication.getAppContext();
    // 单例
    private static AnimationsContainer mInstance;


    public AnimationsContainer(){
    }
    //获取单例
    public static AnimationsContainer getInstance(int resId, int fps) {
        if (mInstance == null){
            mInstance = new AnimationsContainer();
        }
        mInstance.setResId(resId, fps);
        return mInstance;
    }

    public void setResId(int resId, int fps){
        this.resId = resId;
        this.FPS = fps;
    }
//    // 从xml中读取资源ID数组
//    private int[] mProgressAnimFrames = getData(resId);

    /**
     * @param imageView
     * @return progress dialog animation
     */
    public FramesSequenceAnimation createProgressDialogAnim(ImageView imageView) {
        return new FramesSequenceAnimation(imageView, getData(resId), FPS);
    }


    /**
     * 循环读取帧---循环播放帧
     */
    public class FramesSequenceAnimation {
        private int[] mFrames; // 帧数组
        private int mIndex; // 当前帧
        private boolean mShouldRun; // 开始/停止播放用
        private boolean mIsRunning; // 动画是否正在播放,防止重复播放
        private SoftReference mSoftReferenceImageView; // 软引用ImageView,以便及时释放掉
        private Handler mHandler;
        private int mDelayMillis;
        private OnAnimationStoppedListener mOnAnimationStoppedListener; //播放停止监听

        private Bitmap mBitmap = null;
        private BitmapFactory.Options mBitmapOptions;//Bitmap管理类,可有效减少Bitmap的OOM问题

        public FramesSequenceAnimation(ImageView imageView, int[] frames, int fps) {
            mHandler = new Handler();
            mFrames = frames;
            mIndex = -1;
            mSoftReferenceImageView = new SoftReference(imageView);
            mShouldRun = false;
            mIsRunning = false;
            mDelayMillis = 1000 / fps;//帧动画时间间隔,毫秒

            imageView.setImageResource(mFrames[0]);

            // 当图片大小类型相同时进行复用,避免频繁GC
            if (Build.VERSION.SDK_INT >= 11) {
                Bitmap bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
                int width = bmp.getWidth();
                int height = bmp.getHeight();
                Bitmap.Config config = bmp.getConfig();
                mBitmap = Bitmap.createBitmap(width, height, config);
                mBitmapOptions = new BitmapFactory.Options();
                //设置Bitmap内存复用
                mBitmapOptions.inBitmap = mBitmap;//Bitmap复用内存块,类似对象池,避免不必要的内存分配和回收
                mBitmapOptions.inMutable = true;//解码时返回可变Bitmap
                mBitmapOptions.inSampleSize = 1;//缩放比例
            }
        }
        //循环读取下一帧
        private int getNext() {
            mIndex++;
            //设置只播放一次动画
//            if(mIndex==mFrames.length-1){
//                stop();
//            }
            if (mIndex >= mFrames.length)
                mIndex = 0;
            return mFrames[mIndex];
        }

        /**
         * 播放动画,同步锁防止多线程读帧时,数据安全问题
         */
        public synchronized void start() {
            mShouldRun = true;
            if (mIsRunning)
                return;

            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    ImageView imageView = mSoftReferenceImageView.get();
                    if (!mShouldRun || imageView == null) {
                        mIsRunning = false;
                        if (mOnAnimationStoppedListener != null) {
                            mOnAnimationStoppedListener.AnimationStopped();
                        }
                        return;
                    }

                    mIsRunning = true;
                    //新开线程去读下一帧
                    mHandler.postDelayed(this, mDelayMillis);

                    if (imageView.isShown()) {
                        int imageRes = getNext();
                        if (mBitmap != null) { // so Build.VERSION.SDK_INT >= 11
                            Bitmap bitmap = null;
                            try {
                                bitmap = BitmapFactory.decodeResource(imageView.getResources(), imageRes, mBitmapOptions);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            if (bitmap != null) {
                                imageView.setImageBitmap(bitmap);
                            } else {
                                imageView.setImageResource(imageRes);
                                mBitmap.recycle();
                                mBitmap = null;
                            }
                        } else {
                            imageView.setImageResource(imageRes);
                        }
                    }

                }
            };

            mHandler.post(runnable);
        }

        /**
         * 停止播放
         */
        public synchronized void stop() {
            mShouldRun = false;
        }

        /**
         * 设置停止播放监听
         * @param listener
         */
        public void setOnAnimStopListener(OnAnimationStoppedListener listener){
            this.mOnAnimationStoppedListener = listener;
        }
    }

    /**
     * 从xml中读取帧数组
     * @param resId
     * @return
     */
    private int[] getData(int resId){
        TypedArray array = mContext.getResources().obtainTypedArray(resId);

        int len = array.length();
        int[] intArray = new int[array.length()];

        for(int i = 0; i < len; i++){
            intArray[i] = array.getResourceId(i, 0);
        }
        array.recycle();
        return intArray;
    }

    /**
     * 停止播放监听
     */
    public interface OnAnimationStoppedListener{
        void AnimationStopped();
    }

}(4)调用优化后的帧动画:public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button startBtn;
    private ImageView animationImg;
    private AnimationsContainer.FramesSequenceAnimation animation;
    private boolean start = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startBtn=findViewById(R.id.start_btn);
        animationImg=findViewById(R.id.animation_img);
        startBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(animation == null){
            //优化后的帧动画
            animation = AnimationsContainer.getInstance(R.array.loading_anim, 40).createProgressDialogAnim(animationImg);
        }
        if(!switchBtn()){
            animation.start();
        }else {
            animation.stop();
        }
    }

    //控制开关
    private boolean switchBtn(){
        boolean returnV = start;
        start = !start;
        return returnV;
    }

}

此方案亲测可行,流畅,需注意的是fps为帧数而不是时间间隔噢。

你可能感兴趣的:(针对多个帧动画相互切换且帧数较多情况的另一种实现方式,优化方案,流畅不卡顿)