五分钟实战 Compose 展开/收起动画

假设你正在使用 Jetpack Compose 构建一个屏幕界面,并且希望实现某些内容的展开或收起效果 ------ 比如常见问题解答部分、下拉面板或筛选菜单。

你可能会想:

我要如何实现平滑的过渡效果呢?

好消息是:Jetpack Compose 让这个需求变得相当容易实现。

该篇文章,让我为大家详细介绍处理展开/收起动画的所有实用方法 ------ 每种方法都适用于不同的用例。

高度的平滑增长

假设你有一个方块,当用户点击它时,下方会显示一些内容,而你希望整个过程能够自然展开,不出现跳动。

对于这种情况,Jetpack Compose 有一个非常实用的修饰符:animateContentSize()

下面是一个简单的示例:

Kotlin 复制代码
@Composable
fun ExpandableBox() {
    var expanded by remember { mutableStateOf(false) }
    Column(modifier = Modifier
        .clickable { expanded = !expanded }
        .background(Color.DarkGray)
        .padding(16.dp)
        .animateContentSize() // 高度变化产生动画
    ) {
        Text("Q: What is Compose?", color = Color.White)
        if (expanded) {
            Text(
                "A: It's a modern UI toolkit for Android that replaces XML.", color = Color.LightGray, modifier = Modifier.padding(top = 8.dp)
            )
        }
    }
}

只需要这一个修饰符------就这样,每当高度发生变化时,就能实现平滑过渡。

当然,它的工作原理实际上是这样的:

当你将 animateContentSize 应用到一个 Composable 上时,Compose 会监听该组件内容宽高的变化。如果内容尺寸发生变化(例如,因为内部文本变长、子元素增删等),它会自动在旧尺寸和新尺寸之间执行一个平滑的过渡动画。

淡入淡出与滑动

如果你不只是想增加高度,还希望内容在可见时淡入、滑入或放大。

这就来到了 AnimatedVisibility() 的用武之地。

假设你有一个用于显示/隐藏面板的切换按钮。以下是如何巧妙地实现动画效果:

Kotlin 复制代码
@Composable
fun ToggleInfoPanel() {
    var show by remember { mutableStateOf(true) }
    
    Column(modifier = Modifier.padding(16.dp)) {
        Button(onClick = { show = !show }) {
            Text(if (show) "Hide Info" else "Show Info")
        }
        AnimatedVisibility(
            visible = show, enter = fadeIn() + slideInVertically(), exit = fadeOut() + slideOutVertically()
        ) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .background(Color.Magenta)
                    .padding(16.dp)
            ) {
                Text("This is a toggleable info section", color = Color.White)
            }
        }
    }
}

在这里,你控制的是它如何出现或消失,而不仅仅是它是否显示。

AnimatedVisibility() 包裹另一个 Composable,并根据其 visible 的值(true/false)来决定是否显示内部内容。当 visible 状态改变时,它会自动执行指定的进入动画或退出动画。

垂直展开/收起的滑动面板

假设你正在构建一个筛选下拉菜单或手风琴式部分,并且希望它在展开时从顶部向下滑动打开,在收起时向上收缩回去。

你可以在 AnimatedVisibility() 中使用 expandVertically()shrinkVertically()

Kotlin 复制代码
@Composable
fun FilterDropdown() {
    var expanded by remember { mutableStateOf(false) }
    Column(modifier = Modifier.padding(16.dp)) {

        Text("Filters", modifier = Modifier
            .clickable { expanded = !expanded }
            .background(Color.Gray)
            .padding(12.dp), color = Color.White)

        AnimatedVisibility(
            visible = expanded, enter = expandVertically(), exit = shrinkVertically()
        ) {
            Column(
                Modifier
                    .fillMaxWidth()
                    .background(Color.LightGray)
                    .padding(16.dp)
            ) {
                Text("Sort by Price")
                Text("Sort by Rating")
                Text("Sort by Availability")
            }
        }
    }
}

流畅的垂直滑动效果,非常适合可展开的面板。

多个元素的动画

如果你想让高度、背景颜色和透明度在展开时同步进行动画处理,该怎么做呢?

轮到 updateTransition() 大显身手了。它的使用稍微复杂一些,但能让你完全掌控动画效果:

kotlin 复制代码
@Composable
fun FancyExpandBox() {
    var expanded by remember { mutableStateOf(false) }
    val transition = updateTransition(targetState = expanded, label = "transition")
    val txtSize by transition.animateInt(label = "text") {
        if (it) 24 else 14
    }
    val height by transition.animateDp(label = "height") {
        if (it) 200.dp else 60.dp
    }
    val bgColor by transition.animateColor(label = "color") {
        if (it) Color.Blue else Color.Gray
    }
    val alpha by transition.animateFloat(label = "alpha") {
        if (it) 1f else 0.5f
    }
    Box(
        modifier = Modifier
            .clickable { expanded = !expanded }
            .fillMaxWidth()
            .height(height)
            .background(bgColor.copy(alpha = alpha))
            .padding(16.dp)
    ) {
        Text("Tap to expand or collapse", color = Color.White, fontSize = txtSize.sp)
    }
}

该方法需要添加 androidx.compose.animation:animation-core 依赖。

你可以在这里对任何属性进行动画处理 ------ 边距、形状、文字大小 ------ 它们都会一起过渡变化。

updateTransition 是一个状态驱动的动画协调器。它通过观察一个关键状态的变化,自动管理一组相关视觉属性的动画,将它们从旧状态的目标值流畅地过渡到新状态的目标值,非常适合处理复杂的、多步骤的 UI 状态切换动画。

为布局权重做动画

如果你有两个面板并排放置或垂直堆叠,并且希望一个面板变大而另一个面板变小 ------ 那就对权重进行动画处理!

Kotlin 复制代码
@Composable
fun WeightAnimationDemo() {
    var expanded by remember { mutableStateOf(false) }
    val weight by animateFloatAsState(targetValue = if (expanded) 0.8f else 0.3f)
    Column(modifier = Modifier.height(200.dp)) {
        Box(
            modifier = Modifier
                .weight(weight)
                .fillMaxWidth()
                .background(Color.Blue)
                .clickable { expanded = !expanded })
        Box(
            modifier = Modifier
                .weight(1f - weight)
                .fillMaxWidth()
                .background(Color.DarkGray)
        )
    }
}

点击顶部框会平滑地调整两个部分的大小。

总结

  • 如果你的视图只是大小发生增长或缩小 ------ animateContentSize()
  • 如果你想要淡入、滑动或缩放动画 ------ AnimatedVisibility()
  • 对于更高级的布局变化或组合效果 ------ updateTransition()
  • 想要使用布局权重调整大小? ------ animateFloatAsState()

一旦你熟悉了这些工具,你会发现在 Compose 中构建大多数展开/收起的用户界面变得超级直观 ------ 而且它们比使用 XML 时更加流畅,更加直观,更加方便。

相关推荐
4***99744 小时前
Kotlin序列处理
android·开发语言·kotlin
t***D2644 小时前
Kotlin在服务端开发中的生态建设
android·开发语言·kotlin
玲珑Felone4 小时前
flutter 状态管理--InheritedWidget、Provider原理解析
android·flutter·ios
BoomHe5 小时前
车载应用配置系统签名
android·android studio
路人甲ing..7 小时前
用 Android Studio 自带的模拟 Android Emulator 调试
android·java·ide·ubuntu·kotlin·android studio
路人甲ing..7 小时前
Android Studio 模拟器报错 The emulator process for AVD xxxxx has terminated.
android·java·ide·kotlin·android studio
弥巷7 小时前
【Android】 View事件分发机制源码分析
android·java
wanna8 小时前
安卓自学小笔记第一弹
android·笔记