android 解决帧动画卡顿情况
16lz
2021-01-23
最近公司有一个项目需求,需要在一个特定的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">
(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 SoftReferencemSoftReferenceImageView; // 软引用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; }}以上为解决帧动画卡顿的总结和解决方案,如有问题请留言,谢谢各位。
更多相关文章
- Android开发之网络请求通信专题(二):基于HttpClient的文件上传下载
- Android学习札记44:解读APK反编译后得到的布局文件
- Android中Gif图片的显示
- Android实现过渡动画、引导页 Android判断是否第一次启动App
- android文件系统及其制作
- Android中如何反编译apk文件
- Android多媒体--访问网络上的Audio对应的M3U文件,实现网络音频流
- 文件操作二
- Android加载drawable中图片后自动缩放的原理