Android动画系统深度解析:三种动画类型与源码实现

216 阅读4分钟

Android动画系统通过三种核心动画类型(View动画、帧动画、属性动画)实现了丰富的视觉效果。以下从源码角度剖析其原理,并结合实战经验提供优化方案。

一、View动画(补间动画)

1. 核心原理
View动画通过矩阵变换实现平移、缩放、旋转、透明度效果,其源码实现分为两个阶段:

  • 初始化阶段
    Animation.initialize()方法计算动画的变换矩阵,核心逻辑在Camera类中完成三维空间变换。例如,旋转动画会通过Camera.rotateX()生成绕X轴旋转的矩阵。
  • 应用阶段
    每帧调用Animation.applyTransformation(),将矩阵作用于View的Canvas。源码中通过View.getMatrix()获取当前变换矩阵,最终通过Canvas.concat()合并到绘制流程。

2. 关键限制
View动画仅修改绘制效果,不改变View的实际属性。例如,按钮平移后,点击区域仍停留在原位置。源码中通过View.getHitRect()可见,其碰撞检测基于原始布局参数,而非动画后的位置。

3. 实战优化

  • 自定义动画:继承Animation类,重写applyTransformation()实现非线性变换。
  • 布局动画:通过LayoutAnimationController为ViewGroup子View批量添加入场动画,源码中通过递归遍历子View并延迟启动动画实现。

二、帧动画(Drawable动画)

1. 核心原理
帧动画通过AnimationDrawable类管理一系列Drawable资源,按固定间隔切换显示。其源码流程如下:

  • 资源加载
    在XML中定义<animation-list>,解析时通过AnimationDrawable.inflate()加载所有帧资源到mFrame数组。
  • 播放控制
    调用start()时,通过Choreographer注册VSYNC信号回调,以屏幕刷新率(默认10ms)触发帧切换。每帧更新时,调用invalidate()重绘View。

2. 内存风险
帧动画将所有帧加载到内存,易导致OOM。源码中通过AnimationDrawable.setOneShot(true)限制单次播放,但无法根本解决内存问题。

3. 实战优化

  • 资源压缩:使用WebP格式替代PNG,减少单帧体积。
  • 动态加载:通过LruCache缓存最近使用的帧,结合Glide按需加载图片。

三、属性动画(Property Animation)

1. 核心原理
属性动画通过修改对象属性实现动画,其源码架构分为三层:

  • 值计算层
    ValueAnimator根据插值器(如AccelerateDecelerateInterpolator)和估值器(如FloatEvaluator)计算中间值。例如,平移动画中,FloatEvaluator通过线性插值计算当前位置。
  • 属性修改层
    ObjectAnimator通过反射调用setXXX()方法更新属性。源码中通过Property类封装属性名和读写方法,支持自定义属性(需实现get()set())。
  • 动画控制层
    AnimatorSet管理多个动画的时序关系,支持并行(playTogether())和串行(playSequentially())。源码中通过时间轴调度器确保动画同步。

2. 硬件加速
属性动画默认启用硬件加速,通过RenderThread将绘制命令提交给GPU。源码中通过View.setLayerType(LAYER_TYPE_HARDWARE)开启硬件层,减少CPU到GPU的数据拷贝。

3. 实战优化

  • 减少属性查询:对频繁更新的属性(如位置),在onAnimationUpdate()中缓存初始值。
  • 合并动画:使用ViewPropertyAnimator链式调用(如view.animate().x(100f).alpha(0f))减少对象创建开销。

四、性能对比与选型建议

动画类型内存占用灵活性适用场景
View动画简单效果(如按钮点击反馈)
帧动画装饰性动画(如加载指示器)
属性动画复杂交互(如视图过渡、游戏特效)

选型原则

  • 优先使用属性动画,尤其是需要修改实际属性或实现复杂效果时。
  • 对简单效果且无需修改属性的场景,可使用View动画。
  • 帧动画仅适用于帧数少、内存可控的场景。

五、源码调试技巧

  1. 动画卡顿定位
    通过Systrace跟踪Choreographer#doFrame耗时,确认是否因主线程阻塞导致掉帧。
  2. 属性修改验证
    ObjectAnimatoronAnimationUpdate()中打印属性值,确认是否按预期更新。
  3. 硬件加速检查
    通过View.isHardwareAccelerated()确认是否启用硬件加速,未启用时检查AndroidManifest.xml中的hardwareAccelerated属性。

六、总结

Android动画系统的核心在于时间驱动属性变化,三种动画类型分别对应不同场景:

  • View动画:轻量但有限,适合简单视觉效果。
  • 帧动画:实现简单但内存敏感,需谨慎使用。
  • 属性动画:灵活强大,是现代Android动画的首选方案。

通过深入理解源码实现,开发者不仅能编写出更流畅的动画,还能在遇到性能问题时,快速定位到系统层级的根本原因。建议结合AOSP源码中的Animation.javaObjectAnimator.java等核心类进行实战调试,将理论知识转化为真正的开发能力。