最近公司有一个项目需求,需要在一个特定的android设备上做一组帧动画的效果,当时感觉这个功能很容易,相信大家都知道,android的原生帧动画实现方法,但是问题就出现在这,如果使用原生的帧动画,在手机上是没有问题的,但是在公司的设备上就非常卡顿,也就是低端机,由于图片过多,效果异常卡顿,所以在这篇文章中就说一下帧动画的优化问题。

      首先还是先来看一下android原生的帧动画的实现,代码如下:

      (1)帧动画的资源文件 放入res/drawable下

<?xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android">            android:drawable="@drawable/animation2"        android:duration="30"/>            android:drawable="@drawable/animation3"        android:duration="30"/>            android:drawable="@drawable/animation4"        android:duration="30"/>            android:drawable="@drawable/animation5"        android:duration="30"/>            android:drawable="@drawable/animation6"        android:duration="30"/>            android:drawable="@drawable/animation7"        android:duration="30"/>            android:drawable="@drawable/animation8"        android:duration="30"/>            android:drawable="@drawable/animation9"        android:duration="30"/>            android:drawable="@drawable/animation10"        android:duration="30"/>            android:drawable="@drawable/animation11"        android:duration="30"/>            android:drawable="@drawable/animation12"        android:duration="30"/>            android:drawable="@drawable/animation13"        android:duration="30"/>            android:drawable="@drawable/animation14"        android:duration="30"/>            android:drawable="@drawable/animation15"        android:duration="30"/>            android:drawable="@drawable/animation16"        android:duration="30"/>            android:drawable="@drawable/animation17"        android:duration="30"/>            android:drawable="@drawable/animation18"        android:duration="30"/>            android:drawable="@drawable/animation19"        android:duration="30"/>            android:drawable="@drawable/animation20"        android:duration="30"/>            android:drawable="@drawable/animation21"        android:duration="30"/>            android:drawable="@drawable/animation22"        android:duration="30"/>            android:drawable="@drawable/animation23"        android:duration="30"/>            android:drawable="@drawable/animation24"        android:duration="30"/>            android:drawable="@drawable/animation25"        android:duration="30"/>            android:drawable="@drawable/animation26"        android:duration="30"/>            android:drawable="@drawable/animation27"        android:duration="30"/>            android:drawable="@drawable/animation28"        android:duration="30"/>            android:drawable="@drawable/animation29"        android:duration="30"/>            android:drawable="@drawable/animation30"        android:duration="30"/>            android:drawable="@drawable/animation31"        android:duration="30"/>            android:drawable="@drawable/animation32"        android:duration="30"/>            android:drawable="@drawable/animation33"        android:duration="30"/>            android:drawable="@drawable/animation34"        android:duration="30"/>            android:drawable="@drawable/animation35"        android:duration="30"/>            android:drawable="@drawable/animation36"        android:duration="30"/>            android:drawable="@drawable/animation37"        android:duration="30"/>            android:drawable="@drawable/animation38"        android:duration="30"/>            android:drawable="@drawable/animation39"        android:duration="30"/>            android:drawable="@drawable/animation40"        android:duration="30"/>            android:drawable="@drawable/animation41"        android:duration="30"/>            android:drawable="@drawable/animation42"        android:duration="30"/>            android:drawable="@drawable/animation43"        android:duration="30"/>            android:drawable="@drawable/animation44"        android:duration="30"/>            android:drawable="@drawable/animation45"        android:duration="30"/>            android:drawable="@drawable/animation46"        android:duration="30"/>            android:drawable="@drawable/animation47"        android:duration="30"/>            android:drawable="@drawable/animation48"        android:duration="30"/>            android:drawable="@drawable/animation49"        android:duration="30"/>            android:drawable="@drawable/animation50"        android:duration="30"/>            android:drawable="@drawable/animation51"        android:duration="30"/>            android:drawable="@drawable/animation52"        android:duration="30"/>            android:drawable="@drawable/animation53"        android:duration="30"/>            android:drawable="@drawable/animation54"        android:duration="30"/>            android:drawable="@drawable/animation55"        android:duration="30"/>            android:drawable="@drawable/animation56"        android:duration="30"/>            android:drawable="@drawable/animation57"        android:duration="30"/>            android:drawable="@drawable/animation58"        android:duration="30"/>            android:drawable="@drawable/animation59"        android:duration="30"/>            android:drawable="@drawable/animation60"        android:duration="30"/>            android:drawable="@drawable/animation61"        android:duration="30"/>            android:drawable="@drawable/animation62"        android:duration="30"/>            android:drawable="@drawable/animation63"        android:duration="30"/>            android:drawable="@drawable/animation64"        android:duration="30"/>            android:drawable="@drawable/animation65"        android:duration="30"/>            android:drawable="@drawable/animation66"        android:duration="30"/>            android:drawable="@drawable/animation67"        android:duration="30"/>            android:drawable="@drawable/animation68"        android:duration="30"/>            android:drawable="@drawable/animation69"        android:duration="30"/>            android:drawable="@drawable/animation70"        android:duration="30"/>            android:drawable="@drawable/animation71"        android:duration="30"/>

(2)布局文件

<?xml version="1.0" encoding="utf-8"?>    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">            android:id="@+id/start_btn"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="开始动画"/>            android:id="@+id/animation_img"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        />

(3)Java代码实现开启帧动画

//animationImg为ImageView的控件animationImg.setImageResource(R.drawable.animation_list);AnimationDrawable animationDrawable= (AnimationDrawable) animationImg.getDrawable();animationDrawable.start();

以上是android原生实现帧动画的,如果图片比较少可以这样使用,但是图片过多的话在低端机上就会造成卡顿,甚至会出现OOM异常,为了解决这个问题,可以把帧动画的图片一张张的分开,读到哪一张图就加载哪一张图,加载后把上一张图片释放。具体代码如下:

(1)帧动画的资源文件 放入res/values/arrays.xml文件中 如果没有arrays.xml文件就新建一个

<?xml version="1.0" encoding="utf-8"?>    name="loading_anim">        @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否则无效,代码如下:

public class MyApplication extends 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;    }}
以上为解决帧动画卡顿的总结和解决方案,如有问题请留言,谢谢各位。


更多相关文章

  1. Android开发之网络请求通信专题(二):基于HttpClient的文件上传下载
  2. Android学习札记44:解读APK反编译后得到的布局文件
  3. Android中Gif图片的显示
  4. Android实现过渡动画、引导页 Android判断是否第一次启动App
  5. android文件系统及其制作
  6. Android中如何反编译apk文件
  7. Android多媒体--访问网络上的Audio对应的M3U文件,实现网络音频流
  8. 文件操作二
  9. Android加载drawable中图片后自动缩放的原理

随机推荐

  1. Android开发包下载
  2. Android应用安全检测工具简介
  3. 《Android/OPhone 开发完全讲义》已出版,
  4. Android(安卓)APN设置
  5. 对Android体系结构的理解--后续会补充
  6. Android(安卓)1.5 SDK, Release 1 翻译完
  7. Android(安卓)SDK 镜像站
  8. TextView设置跑马灯的样式
  9. Android系列之Android开发教程代码实例
  10. Android(安卓)ScrollView去掉滚动条及Scr