在 Android 应用开发中,优雅的动画效果能够极大地提升用户体验。Android 提供了多种动画系统,而视图动画(View Animation)——也被称为补间动画(Tween Animation)——是最基础也是最常用的动画框架之一。
本文将带你深入理解 Android 视图动画的工作原理,逐一剖析透明度、旋转、平移、缩放这四大经典动画的 API 细节,并结合实际代码演示如何将它们组合使用。
1. 视图动画的核心工作原理
视图动画(View Animation)框架主要定义了透明度(Alpha)、旋转(Rotate)、缩放(Scale)和位移(Translate)几种常见的渐变动画。它只控制 View 的绘制效果,而不会真正改变 View 的属性(如宽高、点击事件响应区域等)。
运行机制
视图动画的渲染 and 驱动主要在 View 绘制流程中完成:
- 矩阵变换:每次绘制子视图时,View 所在的 ViewGroup 中的
drawChild函数会获取该 View 的 Animation 的Transformation值。 - 画布操作:ViewGroup 通过调用
canvas.concat(transformToApply.getMatrix()),利用矩阵运算对 Canvas 进行变换,完成动画帧的绘制。 - 循环驱动:如果动画未播放完毕,系统会继续调用
invalidate()函数,触发下一次重绘,从而源源不断地生成动画帧,直到动画结束。
WARNING
关键局限性(面试高频考点):
视图动画仅仅改变了 View 绘制在屏幕上的视觉影像,而 View 真正的物理坐标和事件响应区域并未发生改变。例如:如果你将一个按钮通过位移动画平移了 300 像素,你的手指去点击新位置的按钮是无效的,必须点击原位置才能触发点击事件。若需要改变实际属性,请使用属性动画(Property Animation)。
2. 四大基础补间动画详解
Android 提供了四个具体的子类来实现基础动画效果。我们在使用时既可以通过 XML 文件定义,也可以直接在 Kotlin/Java 代码中动态构建。

2.1 AlphaAnimation(透明度动画)
透明度动画用于为视图添加淡入淡出效果,非常适合用在页面转场或内容载入时。
构造函数关键参数:
| 参数 | 数据类型 | 意义 |
|---|---|---|
fromAlpha | Float | 动画起始时的透明度。取值范围为 0.0F(完全透明)到 1.0F(完全不透明)。 |
toAlpha | Float | 动画结束时的透明度。取值范围同上。 |
代码实现:
/**
* 演示:将视图从完全透明(0F)渐变至完全不透明(1F)
*/
private fun playAlphaAnimation(targetView: View) {
val alphaAnim = AlphaAnimation(0F, 1F).apply {
duration = 1000 // 动画持续时间,单位毫秒
fillAfter = true // 动画结束后保持结束时的状态
}
targetView.startAnimation(alphaAnim)
}2.2 RotateAnimation(旋转动画)
旋转动画用于让视图绕着某个锚点进行角度旋转。例如:音乐播放器的黑胶唱片旋转、加载中的 Loading 圆圈等。
构造函数关键参数:
| 参数 | 类型 | 意义与取值 |
|---|---|---|
fromDegrees | Float | 旋转开始的角度。 |
toDegrees | Float | 旋转结束的角度(顺时针为正数,逆时针为负数)。 |
pivotXType | Int | X 轴旋转中心点的定位模式。 • Animation.ABSOLUTE:绝对像素值。• Animation.RELATIVE_TO_SELF:相对于 View 自身宽度的百分比。• Animation.RELATIVE_TO_PARENT:相对于父布局宽度的百分比。 |
pivotXValue | Float | X 轴中心点的值。若为百分比模式,取值范围通常为 0.0F ~ 1.0F。 |
pivotYType | Int | Y 轴旋转中心点的定位模式(取值同 pivotXType)。 |
pivotYValue | Float | Y 轴中心点的值(取值同 pivotXValue)。 |
代码实现:
// 示例 1:以 View 左上角 (0, 0) 为中心顺时针旋转 360 度
private fun rotateFromTopLeft(targetView: View) {
val rotateAnim = RotateAnimation(0F, 360F, 0F, 0F).apply {
duration = 1000
}
targetView.startAnimation(rotateAnim)
}
// 示例 2:以 View 自身的正中心为锚点旋转 360 度(最常用)
private fun rotateAroundCenter(targetView: View) {
val rotateAnim = RotateAnimation(
0F,
360F,
Animation.RELATIVE_TO_SELF, 0.5F, // 宽度的 50%
Animation.RELATIVE_TO_SELF, 0.5F // 高度的 50%
).apply {
duration = 1000
repeatCount = Animation.INFINITE // 无限循环
repeatMode = Animation.RESTART // 重新开始播放
}
targetView.startAnimation(rotateAnim)
}2.3 TranslateAnimation(位移动画)
位移动画用于让视图在二维平面上做位置偏移。适合抽屉栏拉出、气泡浮动等场景。
构造函数关键参数:
位移动画需要指定 X 轴和 Y 轴的起始点与结束点,共 8 个参数:
| 参数 | 说明 |
|---|---|
fromXType / toXType | 起始/结束 X 坐标的定位模式(ABSOLUTE / RELATIVE_TO_SELF / RELATIVE_TO_PARENT) |
fromXValue / toXValue | 起始/结束 X 坐标的具体数值 |
fromYType / toYType | 起始/结束 Y 坐标的定位模式 |
fromYValue / toYValue | 起始/结束 Y 坐标的具体数值 |
代码实现:
// 示例:将视图在 1 秒内从左上角相对偏移位移到 (X: 200px, Y: 300px) 的位置
private fun translateView(targetView: View) {
val translateAnim = TranslateAnimation(
Animation.ABSOLUTE, 0F, Animation.ABSOLUTE, 200F,
Animation.ABSOLUTE, 0F, Animation.ABSOLUTE, 300F
).apply {
duration = 1000
fillAfter = true
}
targetView.startAnimation(translateAnim)
}2.4 ScaleAnimation(缩放动画)
缩放动画可以实现视图尺寸的放大或缩小,同样支持指定缩放中心点。
构造函数关键参数:
| 参数 | 意义 | 备注 |
|---|---|---|
fromX / toX | 水平方向的起始/结束缩放比例 | 1.0 代表原始尺寸,0.0 代表缩放到无,2.0 代表放大两倍 |
fromY / toY | 垂直方向的起始/结束缩放比例 | 同上 |
pivotXType / pivotYType | 缩放中心点的类型 | 默认 ABSOLUTE,常用 RELATIVE_TO_SELF |
pivotXValue / pivotYValue | 缩放中心点的值 | 常用 0.5F 表示正中心 |
代码实现:
// 示例 1:以左上角为原点,从 0 倍无缝放大至 2 倍
private fun scaleFromTopLeft(targetView: View) {
val scaleAnim = ScaleAnimation(0F, 2F, 0F, 2F).apply {
duration = 1000
}
targetView.startAnimation(scaleAnim)
}
// 示例 2:以 View 自身的中心为锚点,实现收缩回弹效果
private fun scaleSelfFromCenter(targetView: View) {
val scaleAnim = ScaleAnimation(
0F, 1.0F, 0F, 1.0F,
Animation.RELATIVE_TO_SELF, 0.5F,
Animation.RELATIVE_TO_SELF, 0.5F
).apply {
duration = 1000
// 可以搭配插值器(Interpolator)来实现弹性回弹
interpolator = android.view.animation.OvershootInterpolator()
}
targetView.startAnimation(scaleAnim)
}3. AnimationSet(动画集合)
当我们需要多个动画同时或者按顺序执行时,可以使用 AnimationSet。例如:在位移的过程中,同时进行淡入与放大。
构造函数关键参数:
shareInterpolator:若传入true,则集合中的所有子动画都会共用AnimationSet设置的插值器;若为false,则子动画使用各自独立的插值器。
代码实现:
/**
* 组合动画:位移的同时淡入
*/
private fun playCombinedAnimation(targetView: View) {
// 1. 创建动画集合,并共用同一个插值器
val animationSet = AnimationSet(true).apply {
duration = 1000
fillAfter = true
}
// 2. 创建淡入动画
val alpha = AlphaAnimation(0F, 1F)
animationSet.addAnimation(alpha)
// 3. 创建平移动画
val translate = TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0F, Animation.RELATIVE_TO_SELF, 0.5F,
Animation.RELATIVE_TO_SELF, -1F, Animation.RELATIVE_TO_SELF, 0F
)
animationSet.addAnimation(translate)
// 4. 开始播放集合动画
targetView.startAnimation(animationSet)
}4. 总结与开发建议
- 简单效果首选:如果你只需要简单的页面转场动画、Loading 旋转、控件淡入淡出,且不需要交互(点击事件),视图动画(补间动画)是性能极佳且轻量化的选择。
- 交互控件避坑:如果你的动画控件在移动或缩放后还需要接受用户的点击、滑动等输入事件,请务必使用属性动画(Property Animation),否则会产生点击事件留在原处的尴尬 Bug。
- 生命周期管理:在 Activity 或 Fragment 销毁时,建议调用
view.clearAnimation()清除正在运行的动画,防止可能导致的内存泄漏。