基于SurfaceView实现的帧动画懒加载View
标签搜索

基于SurfaceView实现的帧动画懒加载View

Pop.Kite
2023-11-21 / 0 评论 / 78 阅读 / 正在检测是否收录...

项目地址

Android的帧动画能够通过简单的播片实现众多复杂的动画,但是使用加载动画资源时,Android会将所有的动画资源一次性加载到内存中,在最近的一次工作案例中,由于一个加载帧动画,应用内存占用提高了40MB,由此有了这篇文章。

SurfaceView是View的一个特殊分支,允许在子线程中绘制,而后在主线程显示(很多地图类应用使用),更多的可以自行百度。基于此,实现了一种在子线程中逐帧加载动画资源,然后在主线程显示的帧动画实现。相比原生帧动画,内存仅占用了1MB,但是缺点是会占用CPU资源。如果实际应用,可以根据项目需求,在CPU和内存中选择折中点。

网上使用SurfaceView加载帧动画的案例很多,但是这里给出了封装库,可以做到开箱即用,因为本项目已经开源,所以在阅读源码并且实践后,有所遗漏的地方也可以提交patch以帮助修复,感谢您的支持。

功能支持

对齐方式

在设置视图宽度为match_parent情况下,可以通过setGravity(int mode)选择四种对齐方式:

//居左
int START = 7;
//居右
int END = 8;
//居中
int CENTER = 9;
//填充
int FILL = 10;

循环次数

通过setRepeatCount(int mode)设置两种循环模式:

//仅一次
int ONESHOT = 3;
//无限循环
int INFINITE = 4;

循环模式

已经开启循环情况下

通过setRepeatMode(int mode)设置两种循环模式:

//重新播放
int RESTART = 1;
//倒转播放
int REVERSE = 2;

动画镜像

通过mirror(int mode)设置三种镜像方式:

//去除镜像恢复原始效果
int ORIGIN = 0;
//水平镜像,会镜像对齐方式并翻转位图
int HORIZONTAL = 5;
//垂直镜像,会镜像对齐方式并翻转位图
int VERTICAL = 6;

帧率设置

通过setDuration(long duration)设置两帧之间的绘制间隔。

资源加载

通过setResource(int resId)加载资源,其中resIdstring-array资源,其item是drawable索引

动画播放

通过start()播放动画

动画停止

通过stop()停止动画

简单案例

引入依赖

setting.gradle下添加仓库

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        //...
        maven { url 'https://jitpack.io' }
    }
}

应用的build.gradle下添加依赖

    implementation 'com.github.popkter:PopView:v0.271'

布局资源

    <com.popkter.speechview.FrameAnimatorView
            android:id="@+id/fmv"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="match_parent"
            android:layout_height="498px"/>

动画资源

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="apple_anim">
        <item>@drawable/frame_0</item>
        <item>@drawable/frame_1</item>
        <item>@drawable/frame_2</item>
        <item>@drawable/frame_3</item>
        <item>@drawable/frame_4</item>
        <item>@drawable/frame_5</item>
    </string-array>
</resources>

调用方法

binding.fmv
  //在使用前需要先设置动画资源
  .setResource(R.array.apple_anim)
  .setGravity(FrameAnimatorView.GravityMode.START)
  .setRepeatCount(FrameAnimatorView.RepeatCount.INFINITE)
  .setRepeatMode(FrameAnimatorView.RepeatMode.REVERSE)

binding.start.setOnClickListener {
  binding.fmv.start()
}

binding.stop.setOnClickListener {
  binding.fmv.stop()
}

binding.mirror.setOnClickListener {
  binding.fmv.run {
    stop()
    //仅在动画停止时镜像效果才生效
    mirror(FrameAnimatorView.MirrorMode.HORIZONTAL)
    start()
  }
}

效果

frameAnimatorView.webm

别的想法

  • 目前通过数组存储resource资源,通过cursor获取当前播放的帧,或许可以通过两个队列实现资源的顺序管理
  • 目前通过向Handler延迟抛出Runnable以加载资源,但可以使用HandlerThread实现此功能
  • 由于未知的原因,在应用停止后,内存中会有未被引用的Bitmap对象,在GC后会被回收,暂未查明原因。

如果有更好的解决方案希望不吝赐教。

0

评论 (0)

取消