在 Jetpack Compose 中,Modifier.animateContentSize() 是一个非常实用且优雅的修饰符,专门用来为组件尺寸的变化添加平滑的过渡动画。
一、 animateContentSize 的核心作用
在默认情况下,如果一个 Composable 的内容发生改变导致其尺寸(宽度或高度)发生变化(例如:展开折叠面板、文字由短变长、动态加载出图片等),UI 会瞬间"闪现"到新的大小。
而 Modifier.animateContentSize() 的作用是:
-
自动捕获尺寸变化:当子组件的测量尺寸发生变化时,它会自动介入。
-
平滑过渡动画:它会在旧尺寸和新尺寸之间自动插值,产生平滑的过渡动画(默认使用弹簧动画 spring),避免突兀的跳变。
二、 完整代码例子
下面的例子展示了一个常见的可折叠卡片(Expandable Card)场景。当点击卡片时,文字内容展开或收起,卡片的高度会平滑地拉伸或收缩:
@Composable
fun ExpandableCardExample() {
// 记录卡片是否展开的状态
var expanded by remember { mutableStateOf(false) }
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
// 点击整张卡片切换展开/收起状态
.clickable { expanded = !expanded }
) {
Column(
modifier = Modifier
.fillMaxWidth()
// 【关键点】:在这里调用 animateContentSize()
// 当 Column 内部高度因文字增减而变化时,高度变化会产生平滑动画
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
.padding(16.dp)
) {
Text(
text = "点击这里可以展开/收起内容",
style = MaterialTheme.typography.titleMedium
)
// 当 expanded 为 true 时显示详细内容,为 false 时不显示
if (expanded) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "这是隐藏的详细内容。当状态改变时,由于外层容器应用了 " +
"animateContentSize(),卡片的高度会自动且平滑地伸展出来," +
"而不是瞬间闪现,给用户极佳的视觉体验。",
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
三、 使用时的关键细节(注意事项)
- 链式调用的顺序非常重要:
- 如果你希望内边距(padding)也参与动画,将 animateContentSize() 放在 .padding() 之前。
- 如果你希望内边距保持固定,只有内部文本/子组件区域参与动画,将它放在 .padding() 之后。
- 它和 AnimatedVisibility 的区别:
- animateContentSize 适合同一个容器内由于内容增减导致整体尺寸变化的场景(比如变长文本、动态增减列表项高度)。
- AnimatedVisibility 更适合专门控制某个独立子组件的淡入淡出、展开/折叠显示。