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动画。
- 帧动画仅适用于帧数少、内存可控的场景。
五、源码调试技巧
- 动画卡顿定位:
通过Systrace跟踪Choreographer#doFrame耗时,确认是否因主线程阻塞导致掉帧。 - 属性修改验证:
在ObjectAnimator的onAnimationUpdate()中打印属性值,确认是否按预期更新。 - 硬件加速检查:
通过View.isHardwareAccelerated()确认是否启用硬件加速,未启用时检查AndroidManifest.xml中的hardwareAccelerated属性。
六、总结
Android动画系统的核心在于时间驱动属性变化,三种动画类型分别对应不同场景:
- View动画:轻量但有限,适合简单视觉效果。
- 帧动画:实现简单但内存敏感,需谨慎使用。
- 属性动画:灵活强大,是现代Android动画的首选方案。
通过深入理解源码实现,开发者不仅能编写出更流畅的动画,还能在遇到性能问题时,快速定位到系统层级的根本原因。建议结合AOSP源码中的Animation.java、ObjectAnimator.java等核心类进行实战调试,将理论知识转化为真正的开发能力。