0Pricing
Android Academy · 课时

动画为何重要

用动效引导用户并带来愉悦体验

动画为何重要 是 CoddyKit 上的免费 Android Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。

让动画发挥作用

动画不仅仅是视觉装饰。在优秀的 Android 应用中,动态效果会引导注意力、解释发生了什么变化,并让界面感觉灵敏而生动。

在本课程中,您将学习 Jetpack Compose 的动画 API,从一行代码即可使用的 animate*AsState 辅助函数,到 AnimatedVisibility、过渡效果和基于物理的弹簧动画。

第一课将介绍为什么以及何时应该使用动画,让每个动态效果都有明确的作用。

Compose 以声明式方式实现动画

在 Compose 中,您不需要逐帧手动启动和停止动画。相反,您只需描述一个目标值;每当该值发生变化时,Compose 就会平滑地向它过渡。

这里,方框的颜色由一个布尔状态驱动。点击后状态翻转,颜色会自动平滑变化。

@Composable
fun ColorBox() {
    var selected by remember { mutableStateOf(false) }
    val color by animateColorAsState(
        targetValue = if (selected) Color.Green else Color.Gray,
        label = "box-color"
    )
    Box(
        Modifier
            .size(120.dp)
            .background(color)
            .clickable { selected = !selected }
    )
}

反馈用户操作

动态效果最重要的作用之一是反馈。用户点击按钮或切换开关时,小幅动画可以确认应用已经接收到操作。

瞬间且突兀的变化会让人感觉应用出了问题;150 到 300 毫秒的快速过渡则会让界面更加精致。这里的按钮在按下时会略微缩小。

@Composable
fun PressableButton(onClick: () -> Unit) {
    var pressed by remember { mutableStateOf(false) }
    val scale by animateFloatAsState(
        targetValue = if (pressed) 0.92f else 1f,
        label = "press-scale"
    )
    Button(
        onClick = onClick,
        modifier = Modifier.graphicsLayer {
            scaleX = scale; scaleY = scale
        }
    ) { Text("Tap me") }
}

解释状态变化

界面发生变化时,动画可以帮助用户理解刚刚发生了什么。淡入并滑入的项目会让人理解为“新增”;淡出则表示“移除”。

没有动态效果时,内容可能会突然出现或消失,让用户困惑某个内容究竟是出现了、消失了,还是移动了。

@Composable
fun Banner(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn() + slideInVertically(),
        exit = fadeOut() + slideOutVertically()
    ) {
        Text("Saved successfully!")
    }
}

保持连续性和空间感

动态效果可以保持连续性。卡片展开或元素移动到新位置时,为变化添加动画能让用户保持空间方位感。

Compose 可以使用 Modifier.animateContentSize() 自动为布局变化添加动画,因此方框会随着内容变化平滑地变大或变小。

@Composable
fun ExpandableCard(expanded: Boolean) {
    Column(
        Modifier
            .fillMaxWidth()
            .animateContentSize()
            .padding(16.dp)
    ) {
        Text("Title", style = MaterialTheme.typography.titleMedium)
        if (expanded) {
            Text("Here is the longer body that appears when expanded.")
        }
    }
}

时长和时机很重要

优秀的动画应该迅速完成。大多数界面过渡的时长应大约在 150 到 400 毫秒之间。太慢会让应用显得迟钝;太快则让人察觉不到动态效果。

Compose 允许您使用 tween 和 easing 曲线调整时长和节奏。缓动通过加速和减速让动态效果感觉更加自然。

val offset by animateDpAsState(
    targetValue = if (open) 0.dp else 200.dp,
    animationSpec = tween(
        durationMillis = 300,
        easing = FastOutSlowInEasing
    ),
    label = "panel-offset"
)

不要过度使用动画

动画很有力量,也因此很容易被滥用。过多的动态效果会分散注意力、拖慢用户操作,甚至引起不适。

经验法则:

  • 使用动画来传达信息,而不是装饰界面。
  • 让过渡短促而含蓄。
  • 避免同时为屏幕上的所有内容添加动画。
  • 动画播放期间绝不要阻止用户进行操作。

尊重无障碍需求

一些用户由于对动态效果敏感,会在系统设置中启用移除动画。体贴的应用应尊重这一偏好。

您可以读取动画是否被缩放为零,并相应减少或禁用非必要的动态效果。

@Composable
fun rememberReduceMotion(): Boolean {
    val context = LocalContext.current
    val scale = Settings.Global.getFloat(
        context.contentResolver,
        Settings.Global.ANIMATOR_DURATION_SCALE,
        1f
    )
    return scale == 0f
}

Compose 动画工具箱

Compose 提供了一套分层的动画 API。您将在本课程中逐步接触它们:

  • animate*AsState - 为单个值添加动画(颜色、大小、浮点数)。
  • AnimatedVisibility - 为出现和消失添加动画。
  • updateTransition - 协调多个值同步变化。
  • AnimatedContent - 在不同内容之间进行交叉淡化。
  • Animatable 和 spring - 提供基于物理效果的底层控制。

请先从高层 API 开始,只有在需要更多控制时再深入到底层。

动画跟随状态

理解一切的关键思维模型是:在 Compose 中,动画会跟踪状态。当您修改某个值时,动画 API 会在每一帧将界面平滑地插值到新值。

这意味着您很少需要编写时间线。您只需声明“此属性应等于 X”,Compose 就会为您处理过渡期间的每一帧。

@Composable
fun FadingText(highlighted: Boolean) {
    val alpha by animateFloatAsState(
        targetValue = if (highlighted) 1f else 0.4f,
        label = "text-alpha"
    )
    Text(
        "Status",
        modifier = Modifier.graphicsLayer { this.alpha = alpha }
    )
}

快速预热纯 Kotlin

动画会在不同值之间进行插值。从本质上说,这只是数学:从 0 到 1 的分数 t 将起始值和结束值进行混合。这个概念(线性插值,即 lerp)是您编写的每个动画的基础。

下面这段简短的纯 Kotlin 代码展示了 Compose 每秒多次应用的这一概念。

fun lerp(start: Float, end: Float, t: Float): Float =
    start + (end - start) * t

fun main() {
    for (i in 0..4) {
        val t = i / 4f
        println("t=$t -> ${lerp(0f, 100f, t)}")
    }
}

快速检查

Jetpack Compose 中动画的核心思维模型是什么?

回顾:有意义的运动

现在您已经了解了动画为何重要,以及动画背后的模型:

  • 运动能够提供反馈、解释变化并保持连续性。
  • Compose 动画采用声明式方式,并跟随状态变化。
  • 让过渡保持简短(约 150-400 毫秒)且有明确目的;不要过度使用动画。
  • 请尊重因无障碍需求而减少动态效果的用户。
  • 优先使用高层 API:animate*AsState、AnimatedVisibility、过渡和弹簧。

接下来:使用 animate*AsState 系列为单个值添加动画。

常见问题解答

「动画为何重要」课时是免费的吗?

是的 — 「动画为何重要」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。

「动画为何重要」这节课中我会学到什么?

用动效引导用户并带来愉悦体验 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「动画为何重要」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Android Academy 课中编写并运行代码吗?

能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 动画为何重要
  2. animate*AsState 基础
  3. AnimatedVisibility 与过渡
  4. 手势与弹簧动画
← 返回 Android Academy