跳到主要内容

Compose 动画

问题

Compose 中有哪些动画 API?如何选择合适的动画方案?

答案

1. 动画 API 选择

2. animateXxxAsState(最常用)

状态驱动的单值动画:

@Composable
fun ExpandableCard(isExpanded: Boolean) {
// 高度动画
val height by animateDpAsState(
targetValue = if (isExpanded) 200.dp else 80.dp,
animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy),
label = "height"
)

// 颜色动画
val backgroundColor by animateColorAsState(
targetValue = if (isExpanded) Color.Blue else Color.Gray,
label = "color"
)

Box(
modifier = Modifier
.fillMaxWidth()
.height(height)
.background(backgroundColor, RoundedCornerShape(12.dp))
)
}

3. AnimatedVisibility

控制内容的显示/隐藏动画:

@Composable
fun FloatingMenu(isVisible: Boolean) {
AnimatedVisibility(
visible = isVisible,
enter = fadeIn() + slideInVertically(),
exit = fadeOut() + slideOutVertically()
) {
Column {
MenuItem("编辑")
MenuItem("删除")
MenuItem("分享")
}
}
}

4. AnimatedContent

内容切换动画:

@Composable
fun CounterDisplay(count: Int) {
AnimatedContent(
targetState = count,
transitionSpec = {
if (targetState > initialState) {
// 数字增大:新内容从上方滑入,旧内容从下方滑出
slideInVertically { -it } + fadeIn() togetherWith
slideOutVertically { it } + fadeOut()
} else {
slideInVertically { it } + fadeIn() togetherWith
slideOutVertically { -it } + fadeOut()
}.using(SizeTransform(clip = false))
},
label = "counter"
) { targetCount ->
Text(
text = "$targetCount",
style = MaterialTheme.typography.displayLarge
)
}
}

5. AnimationSpec 类型

Spec说明适用场景
spring()弹簧物理动画(默认)大部分场景
tween()固定时长 + 缓动曲线精确控制时长
keyframes()关键帧动画复杂中间状态
snap()立即跳转,无过渡开关切换
infiniteRepeatable()无限重复加载动画
// 弹簧动画
spring<Float>(dampingRatio = 0.5f, stiffness = 300f)

// 缓动动画
tween<Float>(durationMillis = 300, easing = FastOutSlowInEasing)

// 无限重复
val alpha by rememberInfiniteTransition(label = "pulse").animateFloat(
initialValue = 0.3f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(500),
repeatMode = RepeatMode.Reverse
),
label = "alpha"
)

常见面试问题

Q1: animateXxxAsStateAnimatable 的区别?

答案

  • animateXxxAsState:声明式 API,给定目标值后自动动画到目标。不支持手动控制(停止、跳转)。适合简单的状态驱动动画
  • Animatable:命令式 API,可以在协程中精确控制:snapTo()animateTo()stop()。适合手势驱动或需要精确控制的动画
val offset = remember { Animatable(0f) }
LaunchedEffect(targetOffset) {
offset.animateTo(targetOffset, animationSpec = spring())
}

Q2: updateTransition 有什么用?

答案

updateTransition 在一个状态机中管理多个属性的协调动画:

val transition = updateTransition(targetState = tabState, label = "tab")
val indicatorLeft by transition.animateDp(label = "left") { state -> state.left }
val indicatorWidth by transition.animateDp(label = "width") { state -> state.width }
val color by transition.animateColor(label = "color") { state -> state.color }

所有属性会同步开始和结束,确保动画一致性。

Q3: Compose 动画如何调试和预览?

答案

通过 Android Studio 的 Animation Preview 工具:

  • @Preview 中使用 updateTransition,Studio 可以逐帧预览
  • 使用 label 参数命名动画,方便在 Animation Inspector 中识别
  • Modifier.animateContentSize()finishedListener 可以监听动画结束

相关链接