在 Jetpack Compose 中,AnimatedContent 是 Crossfade 的"终极进阶版"。如果说 Crossfade 只能做简单的交叉淡入淡出,那么 AnimatedContent 则允许你为不同内容或状态之间的切换定制极其丰富的入场(Enter)与出场(Exit)动画组合。
它非常适合用于数字计数器滚动、多步骤引导页(Wizard)切换、或者带有方向感的 Tab 页面切换。
一、 核心作用与特点
- 灵活的入场与出场组合 (transitionSpec)
你可以根据"旧状态"与"新状态"的变化关系(比如数字变大还是变小、向左切还是向右切),动态决定是让新内容从上方滑入、下方滑入,还是配合缩放和透明度渐变。 - 自动容器尺寸动画 (SizeTransform)
当切换前后的两个布局组件尺寸大小不一致时,AnimatedContent 能够自动、平滑地过渡外层容器的宽高,而不会产生突兀的瞬间跳变。 - 基于状态驱动
和 Crossfade 类似,它接受一个 targetState。只要这个状态发生变化,它就会自动卸载旧内容、渲染新内容并播放配置好的过渡动画。
二、 典型使用示例(带方向感知的计数器)
以下代码展示了如何使用 AnimatedContent 实现一个数字计数器:当数字增大时新数字从下方滑入,减小时从上方滑入:
@Composable
fun AnimatedCounterDemo() {
var count by remember { mutableStateOf(0) }
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 控制按钮
Row(verticalAlignment = Alignment.CenterVertically) {
Button(onClick = { count-- }) { Text("- 减少") }
Spacer(modifier = Modifier.width(16.dp))
Button(onClick = { count++ }) { Text("+ 增加") }
}
Spacer(modifier = Modifier.height(24.dp))
// 使用 AnimatedContent 处理内容切换
AnimatedContent(
targetState = count,
transitionSpec = {
// 根据数值大小判断动画方向
if (targetState > initialState) {
// 变大:新内容从下方滑入并淡入,旧内容向上方滑出并淡出
(slideInVertically { height -> height } + fadeIn()) togetherWith
(slideOutVertically { height -> -height } + fadeOut())
} else {
// 变小:新内容从上方滑入并淡入,旧内容向下方滑出并淡出
(slideInVertically { height -> -height } + fadeIn()) togetherWith
(slideOutVertically { height -> height } + fadeOut())
}.using(
SizeTransform(clip = false) // 允许动画过程中超出裁剪区域
)
},
label = "CounterAnimation"
) { targetCount ->
// 根据 targetCount 渲染对应的内容
Text(
text = "$targetCount",
fontSize = 48.sp,
fontWeight = FontWeight.Bold,
color = Color.Blue
)
}
}
}