一、Android 动画的作用

动画不是单纯为了“好看”,它更多是为了让用户理解界面变化。

比如:

  • 按钮点击后有缩放反馈,用户知道自己点中了;

  • 页面跳转时有过渡动画,用户知道自己从哪里来到哪里;

  • 列表项展开时有高度变化,用户知道内容是“展开”出来的;

  • 加载动画可以缓解等待焦虑;

  • 拖拽、滑动、弹性回弹可以增强真实感。

所以 Android 动画的核心目的可以总结为:

让界面变化更自然,让用户更容易理解状态变化。


二、Android 动画体系总览

Android 动画大体可以分为两套体系:

动画类型

所属体系

典型 API

适用场景

View Animation

传统 View 动画

AnimationTranslateAnimationAlphaAnimation

简单平移、缩放、旋转、透明度

Property Animation

属性动画

ValueAnimatorObjectAnimatorAnimatorSet

改变对象真实属性,最常用

Drawable Animation

帧动画

AnimationDrawable

一帧一帧播放图片

Transition Animation

过渡动画

TransitionTransitionManager

布局变化、场景切换

Physics Animation

物理动画

SpringAnimationFlingAnimation

弹簧、惯性、拖拽回弹

MotionLayout

约束动画

MotionLayout

复杂 UI 状态过渡

Compose Animation

Compose 动画

animate*AsStateAnimatedVisibilityTransition

Jetpack Compose UI 动画

如果是传统 View 项目,最重要的是 Property Animation

如果是新项目使用 Jetpack Compose,最重要的是 Compose Animation


三、View Animation:早期补间动画

View Animation 是 Android 早期的动画系统,也叫补间动画。

所谓补间动画,就是你只需要告诉系统:

  • 开始状态;

  • 结束状态;

  • 动画时间;

  • 动画变化方式。

然后系统会自动计算中间过程。

常见类型有:

类型

作用

alpha

透明度动画

translate

平移动画

scale

缩放动画

rotate

旋转动画

set

动画集合

示例:

<!-- res/anim/fade_in.xml -->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0"
    android:toAlpha="1"
    android:duration="300" />

代码使用:

val animation = AnimationUtils.loadAnimation(context, R.anim.fade_in)
view.startAnimation(animation)

View Animation 的问题

View Animation 最大的问题是:

它改变的是 View 的“显示效果”,不一定改变 View 的真实属性。

比如一个按钮从左边平移到了右边,但它真实的点击区域可能还在原来的位置。

这就是为什么后来 Android 更推荐使用属性动画。


四、Property Animation:属性动画

Property Animation 是 Android 动画中最核心的一套机制。

它的思想是:

在一段时间内,不断修改某个对象的属性值。

例如:

  • 修改 translationX,让 View 水平移动;

  • 修改 alpha,让 View 渐隐渐显;

  • 修改 scaleX / scaleY,让 View 缩放;

  • 修改 rotation,让 View 旋转;

  • 修改自定义对象的属性,实现业务动画。

属性动画常用类:

作用

ValueAnimator

只负责产生数值,不直接修改对象

ObjectAnimator

在产生数值的同时,自动修改对象属性

AnimatorSet

组合多个动画

PropertyValuesHolder

同时修改多个属性

TypeEvaluator

自定义数值计算规则

Interpolator

控制动画速度曲线


五、ValueAnimator

ValueAnimator 是属性动画的基础。

它本身不关心你要改哪个 View,也不直接修改属性。它只负责在指定时间内产生一系列变化中的值。

示例:

val animator = ValueAnimator.ofFloat(0f, 1f)
animator.duration = 300

animator.addUpdateListener { animation ->
    val value = animation.animatedValue as Float
    view.alpha = value
}

animator.start()

这段代码的含义是:

  1. 0f 变化到 1f

  2. 动画持续 300 毫秒;

  3. 每一帧回调一次;

  4. 手动把当前值设置给 view.alpha

所以 ValueAnimator 更底层,也更灵活。

它适合用在:

  • 自定义 View;

  • 自定义绘制;

  • 非 View 对象动画;

  • 需要自己控制每一帧变化的场景。


六、ObjectAnimator

ObjectAnimatorValueAnimator 的子类,但它更方便。

它不仅会计算动画值,还会自动调用对象的 setter 方法修改属性。

示例:

ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply {
    duration = 300
    start()
}

这段代码会自动修改:

view.setAlpha(value)

再比如平移动画:

ObjectAnimator.ofFloat(view, "translationX", 0f, 300f).apply {
    duration = 500
    start()
}

常见可动画属性:

属性

作用

alpha

透明度

translationX

X 方向平移

translationY

Y 方向平移

scaleX

X 方向缩放

scaleY

Y 方向缩放

rotation

旋转

rotationX

绕 X 轴旋转

rotationY

绕 Y 轴旋转

x

View 的 X 坐标

y

View 的 Y 坐标

注意:

ObjectAnimator 依赖属性名和 setter 方法。

比如:

ObjectAnimator.ofFloat(obj, "progress", 0f, 100f)

对象里最好有:

fun setProgress(value: Float)

或者 Kotlin 属性:

var progress: Float = 0f

七、AnimatorSet:组合动画

有时候一个动画不是单一变化,而是多个动画组合。

比如一个按钮先缩小,再放大,同时透明度变化。

这时可以使用 AnimatorSet

示例:

val scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.8f, 1f)
val scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.8f, 1f)
val alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0.5f, 1f)

AnimatorSet().apply {
    playTogether(scaleX, scaleY, alpha)
    duration = 300
    start()
}

常见方法:

方法

作用

playTogether()

多个动画同时执行

playSequentially()

多个动画依次执行

play(a).with(b)

a 和 b 同时执行

play(a).before(b)

a 在 b 之前执行

play(a).after(b)

a 在 b 之后执行


八、Interpolator:插值器

Interpolator 用来控制动画的速度变化。

例如:

  • 匀速;

  • 先慢后快;

  • 先快后慢;

  • 回弹;

  • 超出再回来。

它解决的问题不是“值从哪里到哪里”,而是:

动画过程中的速度怎么变化。

常见插值器:

插值器

效果

LinearInterpolator

匀速

AccelerateInterpolator

加速

DecelerateInterpolator

减速

AccelerateDecelerateInterpolator

先加速后减速

OvershootInterpolator

超出后回弹

BounceInterpolator

弹跳效果

AnticipateInterpolator

先反向蓄力再运动

示例:

ObjectAnimator.ofFloat(view, "translationY", 0f, 500f).apply {
    duration = 600
    interpolator = DecelerateInterpolator()
    start()
}

一个简单理解:

Animator 决定“数值怎么变”,Interpolator 决定“变快还是变慢”。


九、TypeEvaluator:估值器

Interpolator 决定时间进度,TypeEvaluator 决定具体数值。

比如动画从 0100,当前进度是 50%,那么结果通常是 50

但是如果动画对象不是普通数字,而是颜色、坐标点、自定义对象,就需要 TypeEvaluator 计算中间值。

例如颜色动画:

val animator = ValueAnimator.ofArgb(Color.RED, Color.BLUE)
animator.duration = 500
animator.addUpdateListener {
    view.setBackgroundColor(it.animatedValue as Int)
}
animator.start()

自定义对象时,可以实现自己的 TypeEvaluator


十、ViewPropertyAnimator

如果只是对 View 做简单动画,可以用 view.animate()

示例:

view.animate()
    .translationX(300f)
    .alpha(0.5f)
    .setDuration(300)
    .start()

它的优点是写法简单,链式调用,非常适合简单 View 动画。

常见用法:

view.animate()
    .scaleX(1.2f)
    .scaleY(1.2f)
    .setDuration(200)
    .withEndAction {
        view.animate()
            .scaleX(1f)
            .scaleY(1f)
            .start()
    }
    .start()

实际开发中,如果只是做 View 的透明度、缩放、平移,ViewPropertyAnimator 非常好用。


十一、Drawable Animation:帧动画

帧动画就是一张一张图片连续播放,类似 GIF。

XML 示例:

<!-- res/drawable/loading_anim.xml -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item
        android:drawable="@drawable/loading_1"
        android:duration="100" />

    <item
        android:drawable="@drawable/loading_2"
        android:duration="100" />

    <item
        android:drawable="@drawable/loading_3"
        android:duration="100" />

</animation-list>

代码启动:

imageView.setBackgroundResource(R.drawable.loading_anim)
val animation = imageView.background as AnimationDrawable
animation.start()

帧动画的问题是:

  • 图片多时占内存;

  • 不够灵活;

  • 不适合复杂交互动画。

现在很多场景会使用 Lottie、AnimatedVectorDrawable 或 Compose 动画替代。


十二、Transition Animation:过渡动画

Transition 主要用于布局变化。

它关心的是:

从一个界面状态切换到另一个界面状态时,中间过程如何过渡。

常见场景:

  • View 显示 / 隐藏;

  • View 大小变化;

  • View 位置变化;

  • Activity 共享元素动画;

  • Fragment 切换动画。

简单示例:

TransitionManager.beginDelayedTransition(rootView)

view.visibility = View.GONE

执行后,布局变化不会突然发生,而是带有过渡效果。

常见 Transition:

Transition

作用

Fade

淡入淡出

Slide

滑入滑出

Explode

扩散效果

ChangeBounds

位置和大小变化

AutoTransition

默认组合过渡

Transition 适合处理“布局状态变化”,而不是单个属性的精细控制。


十三、MotionLayout

MotionLayoutConstraintLayout 的子类,适合做复杂 UI 状态过渡。

它可以理解为:

用声明式方式描述两个布局状态之间的动画。

例如:

  • 折叠头部;

  • AppBar 联动;

  • 卡片展开;

  • 引导页复杂动效;

  • 复杂手势拖拽动画。

MotionLayout 的核心概念:

概念

说明

MotionLayout

执行动画的布局容器

MotionScene

动画描述文件

ConstraintSet

起始 / 结束约束状态

Transition

状态之间如何过渡

KeyFrame

关键帧控制

MotionLayout 的优点是适合复杂动画,但缺点是学习成本较高。

普通透明度、平移、缩放,不一定需要 MotionLayout。


十四、Physics Animation:物理动画

普通动画更多是“时间驱动”。

比如:

300ms 内从 A 到 B。

物理动画更像是“力驱动”。

比如:

  • 弹簧回弹;

  • 惯性滑动;

  • 拖拽释放后的自然运动。

常见 API:

API

作用

SpringAnimation

弹簧动画

FlingAnimation

惯性动画

DynamicAnimation

物理动画基类

示例:

val springAnim = SpringAnimation(view, DynamicAnimation.TRANSLATION_Y, 0f)
springAnim.spring.stiffness = SpringForce.STIFFNESS_LOW
springAnim.spring.dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
springAnim.start()

物理动画适合做:

  • 下拉回弹;

  • 卡片拖拽;

  • 滑动释放;

  • 弹性按钮;

  • 手势跟随动画。


十五、Jetpack Compose 动画

如果项目使用 Jetpack Compose,动画思路和传统 View 不太一样。

传统 View 动画通常是:

找到 View,然后修改它的属性。

Compose 动画通常是:

状态变化后,UI 根据状态自动重组,动画负责让状态变化变得平滑。

也就是说,Compose 动画更强调“状态驱动”。


十六、animate*AsState

animate*AsState 是 Compose 中最常用、最简单的动画 API。

它适合一个状态值的平滑变化。

示例:

@Composable
fun LikeButton(selected: Boolean) {
    val scale by animateFloatAsState(
        targetValue = if (selected) 1.2f else 1f,
        label = "scale"
    )

    Icon(
        imageVector = Icons.Default.Favorite,
        contentDescription = null,
        modifier = Modifier.scale(scale)
    )
}

selected 改变时,scale 不会瞬间变成目标值,而是平滑过渡。

常见 API:

API

作用

animateFloatAsState

Float 动画

animateDpAsState

Dp 动画

animateColorAsState

颜色动画

animateSizeAsState

Size 动画

animateOffsetAsState

Offset 动画


十七、AnimatedVisibility

AnimatedVisibility 用来处理显示和隐藏动画。

示例:

AnimatedVisibility(
    visible = visible,
    enter = fadeIn() + slideInVertically(),
    exit = fadeOut() + slideOutVertically()
) {
    Text("Hello Android")
}

它适合:

  • 弹窗内容出现;

  • 列表项显示 / 隐藏;

  • 错误提示显示;

  • 空状态页面切换;

  • 卡片展开内容。


十八、AnimatedContent

AnimatedContent 用来处理内容切换。

比如数字变化、状态页面切换。

示例:

AnimatedContent(
    targetState = count,
    label = "count animation"
) { targetCount ->
    Text(text = "Count: $targetCount")
}

适合场景:

  • 数字滚动变化;

  • Tab 内容切换;

  • 加载 / 成功 / 失败状态切换;

  • 页面局部状态切换。


十九、Transition

如果多个动画值要根据同一个状态一起变化,可以使用 Transition

示例:

val transition = updateTransition(
    targetState = expanded,
    label = "card transition"
)

val height by transition.animateDp(label = "height") {
    if (it) 200.dp else 80.dp
}

val alpha by transition.animateFloat(label = "alpha") {
    if (it) 1f else 0.5f
}

它适合:

  • 卡片展开;

  • 多个属性同步变化;

  • 状态机式动画;

  • 稍复杂的 Compose 动画。


二十、InfiniteTransition

InfiniteTransition 用来做无限循环动画。

比如:

  • loading 动画;

  • 呼吸灯;

  • 骨架屏闪烁;

  • 雷达扫描;

  • 波纹扩散。

示例:

val infiniteTransition = rememberInfiniteTransition(label = "loading")

val alpha by infiniteTransition.animateFloat(
    initialValue = 0.3f,
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(800),
        repeatMode = RepeatMode.Reverse
    ),
    label = "alpha"
)

二十一、传统 View 动画和 Compose 动画对比

对比点

传统 View 动画

Compose 动画

编程模型

命令式

声明式

核心思路

操作 View

状态驱动 UI

常用 API

ObjectAnimatorValueAnimator

animate*AsStateTransition

适合项目

XML / View 项目

Compose 项目

动画目标

View 属性

Compose 状态

复杂联动

MotionLayout / AnimatorSet

Transition / AnimationSpec

简单理解:

View 动画是“我命令这个 View 动起来”。
Compose 动画是“状态变了,界面自然过渡过去”。


二十二、动画性能优化

动画性能最核心的目标是:

尽量避免卡顿,保证每一帧都能按时绘制。

常见优化建议:

1. 优先使用 transform 类属性

例如:

  • translationX

  • translationY

  • scaleX

  • scaleY

  • rotation

  • alpha

这些属性通常比频繁改布局参数更适合动画。

不推荐频繁动画修改:

  • width

  • height

  • margin

  • padding

  • 复杂布局约束

因为这些可能触发布局重新测量和重新布局。


2. 避免在动画回调里做重活

不要在 addUpdateListener 里做:

  • 大量计算;

  • 网络请求;

  • 数据库操作;

  • 创建大量对象;

  • 复杂集合遍历。

动画回调可能每一帧都会执行,里面的代码越重,越容易掉帧。


3. 控制动画时长

常见 UI 动画时长:

场景

推荐感觉

点击反馈

100ms - 200ms

普通状态切换

200ms - 300ms

页面过渡

300ms - 500ms

复杂引导动画

可更长,但不要拖沓

动画不是越长越高级,太长反而会让用户觉得慢。


4. 生命周期中及时取消动画

View 被销毁后,如果动画还在执行,可能导致:

  • 内存泄漏;

  • 无效回调;

  • 页面关闭后动画继续执行;

  • 状态错乱。

可以在合适位置取消:

animator.cancel()
view.animate().cancel()

在 RecyclerView 中尤其要注意 item 复用问题。


5. RecyclerView 中要小心动画

RecyclerView 的 item 会复用,如果动画状态没有重置,可能出现:

  • 某些 item 透明度异常;

  • 某些 item 缩放异常;

  • 滚动回来状态不对;

  • 动画重复执行。

绑定数据时最好明确设置初始状态:

holder.itemView.alpha = 1f
holder.itemView.scaleX = 1f
holder.itemView.scaleY = 1f

二十三、常见业务场景怎么选动画方案

场景

推荐方案

按钮点击缩放

ViewPropertyAnimator / Compose animateFloatAsState

View 淡入淡出

ObjectAnimator / Compose AnimatedVisibility

自定义进度条

ValueAnimator / Compose animateFloatAsState

布局展开收起

TransitionManager / Compose AnimatedVisibility

Activity 共享元素

Activity Transition

复杂头部折叠

MotionLayout

拖拽回弹

SpringAnimation

加载动画

AnimationDrawable、Lottie、Compose InfiniteTransition

Compose 状态切换

AnimatedContent / Transition


二十四、面试常问点

1. View Animation 和 Property Animation 有什么区别?

View Animation 改变的是 View 的显示效果,不一定改变真实属性。

Property Animation 改变的是对象的真实属性。

比如一个按钮通过 View Animation 平移后,看起来位置变了,但点击区域可能还在原地。Property Animation 修改 translationX 等真实属性,表现和交互更一致。


2. ValueAnimator 和 ObjectAnimator 有什么区别?

ValueAnimator 只负责产生动画值,不会自动修改对象。

ObjectAnimator 会在产生动画值的同时,自动修改目标对象的属性。

所以:

  • 需要完全自定义控制时,用 ValueAnimator

  • 只是修改某个对象属性时,用 ObjectAnimator


3. Interpolator 和 TypeEvaluator 有什么区别?

Interpolator 决定动画进度的变化速度。

TypeEvaluator 决定根据当前进度计算出什么值。

简单说:

Interpolator 管时间曲线。
TypeEvaluator 管数值计算。


4. 为什么动画会卡顿?

常见原因:

  • 主线程任务太重;

  • 动画回调里做复杂计算;

  • 频繁 requestLayout;

  • 布局层级太深;

  • 图片过大;

  • 创建大量临时对象导致 GC;

  • RecyclerView item 动画状态没有处理好。


5. 为什么推荐属性动画?

因为属性动画更灵活,可以作用于任何对象,不局限于 View。

它也能真正改变对象属性,更适合现代 Android UI 开发。


二十五、总结

Android 动画可以按开发方式分成两类:

第一类是传统 View 动画:

  • View Animation:早期补间动画;

  • Property Animation:最核心,最常用;

  • Drawable Animation:帧动画;

  • Transition:布局过渡;

  • MotionLayout:复杂状态动画;

  • Physics Animation:物理动画。

第二类是 Compose 动画:

  • animate*AsState:单个状态动画;

  • AnimatedVisibility:显示隐藏动画;

  • AnimatedContent:内容切换动画;

  • Transition:多个属性同步动画;

  • InfiniteTransition:无限循环动画。

学习 Android 动画时,不要只记 API,更重要的是理解:

动画的本质是状态变化的可视化。
好动画不是炫技,而是让用户更容易理解界面变化。