🎈 animateContentSize

Compose 动画过渡
🧸 内容大小变化 · 文本展开收起 📐 第4课 · 实战开发

什么是 animateContentSize?

✨ 核心修饰符 Modifier.animateContentSize() 是 Jetpack Compose 中用于监听组件尺寸变化并自动生成平滑过渡动画的利器。当内容大小改变(例如文本行数增减、图片缩放、布局展开/收起),它会驱动尺寸以弹簧或补间动画渐变,而非突兀跳变。

特性说明
🎯 动画类型尺寸插值 (Size interpolation) + 弹簧效果 (Spring)
⚙️ 参数animationSpec 自定义 (默认 spring())
🧩 适用场景文本展开/收起、卡片扩展、列表项动态高度
📦 依赖无需额外库,compose.animation 内置

文本展开/收起 · 代码示例

👇 点击按钮切换 展开/收起,animateContentSize 让文本区域平滑伸缩。

@Composable
fun ExpandableText() {
    var expanded by remember { mutableStateOf(false) }
    val maxLines = if (expanded) Int.MAX_VALUE else 3

    Column(
        modifier = Modifier
            .fillMaxWidth()
            .animateContentSize(  // ✨ 核心动画
                animationSpec = spring(
                    dampingRatio = Spring.DampingRatioMediumBouncy,
                    stiffness = Spring.StiffnessLow
                )
            )
            .padding(16.dp)
            .background(Color(0xFFF7F0E6), shape = RoundedCornerShape(20.dp))
    ) {
        Text(
            text = longArticle,  // 长文本内容
            maxLines = maxLines,
            overflow = TextOverflow.Ellipsis,
            style = MaterialTheme.typography.bodyLarge
        )
        Spacer(modifier = Modifier.height(8.dp))
        TextButton(onClick = { expanded = !expanded }) {
            Text(if (expanded) "👆 收起" else "👇 展开更多")
        }
    }
}

💡 当 maxLines 变化 → 组件高度改变 → animateContentSize 自动驱动过渡动画,无需手动计算。

动画规格 · 参数调优

通过 animationSpec 控制弹性、时长。推荐组合:

参数效果推荐值
dampingRatio阻尼比 (越小越弹)0.3 ~ 0.6 (bouncy)
stiffness刚度 (越大越快)StiffnessLow ~ StiffnessMedium
Spring默认弹簧动画spring() 即可
tween补间动画 (更线性)tween(300, easing = FastOutSlowIn)

🎨 中小学生色系建议:配合柔和背景 (如 #FFF2E5) 和圆润卡片,让动画更亲切。

动画工作流 · SVG 架构

📐 状态变化 expanded 切换 ✨ animateContentSize 弹簧动画 · 尺寸插值 📏 平滑过渡 高度/宽度连续变化 🎒 📘

⬆️ 状态驱动 → 修饰符拦截尺寸变化 → 自动生成流畅动画

进阶技巧 & 注意事项

完整可运行 · 迷你 playground

将以下代码复制到 Compose 项目中即可体验:

@Preview
@Composable
fun AnimatedSizePreview() {
    var isExpanded by remember { mutableStateOf(false) }
    Surface(color = Color(0xFFFEF7EF)) {
        Column(
            modifier = Modifier
                .padding(24.dp)
                .animateContentSize(spring(dampingRatio = 0.5f, stiffness = 300f))
                .background(Color(0xFFFFF0E0), RoundedCornerShape(28.dp))
                .padding(20.dp)
        ) {
            Text(
                text = "🌟 在中小学生色系下,动画更活泼!\nCompose 让过渡变得简单又有趣。",
                maxLines = if (isExpanded) Int.MAX_VALUE else 2,
                overflow = TextOverflow.Ellipsis,
                fontSize = 18.sp,
                color = Color(0xFF2D4A4A)
            )
            Spacer(Modifier.height(12.dp))
            TextButton(onClick = { isExpanded = !isExpanded }) {
                Text(if (isExpanded) "🔼 收起" else "🔽 展开", fontWeight = FontWeight.Bold)
            }
        }
    }
}