用TextureView或SurfaceView 高性能播放帧动画,避免在很多帧的情况下使用AnimationDrawable带来的OOM和卡顿问题。
华为 mate 20X 1920×1080 24bit color JPG 201frames 24fps 测试效果
- project gradle
//...
repositories {
//...
jcenter()
}
//...
- module gradle
implementation 'com.yuyashuai.frameanimation:frameanimation:2.3.6'
<com.yuyashuai.frameanimation.FrameAnimationView
android:id="@+id/animationView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
务必将AnimationView
的生命周期与所在的Activity
或Fragment
绑定
override fun onPause() {
animationView.onPause()
super.onPause()
}
override fun onResume() {
super.onResume()
animationView.onResume()
}
不绑定,在播放的过程中进行页面跳转,可能会导致crash。
//传入文件路径
animationView.playAnimationFromFile("zone720p")
//传入文件夹在assets中的路径
animationView.playAnimationFromAssets("zone720p")
通过调用playAnimation(paths: MutableList<FrameAnimation.PathData>)
方法实现自定义资源的播放。
例:将assets中多个目录的动画合并为一个播放
val paths = FrameAnimationUtil.getPathList(applicationContext, "zone720p", "traffic720p")
animationView.playAnimation(paths)
通过animationView.setRepeatMode()
,设置循环模式,内置了5种循环模式,你也可以参考RepeatMode中的实现,通过实现RepeatStrategy
接口来定义循环播放策略。
内置的5种循环播放模式,以正常顺序为1, 2, 3, 4, 5为例
-
animationView.setRepeatMode(FrameAnimation.RepeatMode.INFINITE)
重复播放: 播放顺序:1, 2, 3, 4, 5, 1, 2, 3, 4, 5...
-
animationView.setRepeatMode(FrameAnimation.RepeatMode.ONCE)
单次播放: 播放顺序:1, 2, 3, 4, 5
-
animationView.setRepeatMode(FrameAnimation.RepeatMode.REVERSE_INFINITE)
往复循环: 播放顺序1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5...
-
animationView.setRepeatMode(FrameAnimation.RepeatMode.REVERSE_ONCE)
往复一次: 播放顺序1, 2, 3, 4, 5, 4, 3, 2, 1
-
animationView.setRepeatMode(RepeatTail(3))
从指定帧开始循环播放尾部循环: 播放顺序1, 2, 3, 4, 5, 3, 4, 5, 3, 4, 5...
由于没有对传入文件夹中的非图片文件进行过滤,请保证传入文件夹内皆为有效图片,否则会造成crash。
animationView.setScaleType()
参考ImageView
的缩放模式,该设置播放中立即生效,也可以通过setMatrix(matrix)
直接设置变换矩阵。
单位ms
animationView.setFrameInterval(12)
帧间隔默认为42ms≈24fps,具体播放速度受设备性能影响,如果设置过小,会以设备能达到的最快速度播放,该设置播放中立即生效
animationView.restoreEnable=true
当View不可见时(包含页面跳转等情况),将自动停止播放并释放部分资源。
停止播放时会保留上次的播放记录,再次进入默认会自动恢复播放,如果不想自动恢复,可以设置restoreEnable=false
。
图片复用能够避免内存抖动,但是要求所有图片大小(分辨率与色位)必须一致(或后一帧的大小总是小于前一帧),因此强烈建议所有帧图片的大小都一致。
animationView.setSupportInBitmap(true)
默认为ture,当设置为false时,将带来显著的内存抖动。如果你的所有帧图片大小一致,则不用设置,如果不一致请设置为false。
Ps. 如果设置为ture但是图片大小不一致会导致复用失败,然后再次尝试以不复用的方式加载图片,所以图片大小不一致的情况下设置为false能带来性能的提升。
animationView.freezeLastFrame(true)
动画播放结束后,将会保留最后一帧,默认清屏。
animationView.setAnimationListener()
- 不兼容
RecyclerView
或者ListView
进行兼容。不要在RecyclerView
或者ListView
中使用 - 务必将
AnimationView
的生命周期与所在的Activity
或Fragment
绑定 - 如果在
Dialog
或PopupWindow
等拥有单独window
的容器中播放,需设置animationView.autoRelease=false
,以保证dismiss
后可以再次播放。
TextureView必须运行在支持硬件加速的上,与SurfaceView 不同,可以和常规View进行变换等操作,更多请参考官方Wiki.
有问题加issue。
对于复杂的动效,帧动画体积大、内场占用高、CPU占用高,可能是很差强人意的解决方案了。如果你们开发、设计资源允许的话,或许你可以采用更好的解决方案。比如
但是上面的解决方案对于设计来说的确又是一个考验,像lottie要求用必须用AE设计动画并导出(劝退了一波用Flash/Animator的人),而且要求AE素材中不能有非矢量图,不然多帧一样OOM(又劝退了一波用AE的,比如我们很多AE的素材直接来自PS而不是AI)。