五分钟实战 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 时更加流畅,更加直观,更加方便。

相关推荐
介一安全1 分钟前
【Frida Android】实战篇15:Frida检测与绕过——基于/proc/self/maps的攻防实战
android·网络安全·逆向·安全性测试·frida
hhy_smile8 分钟前
Android 与 java 设计笔记
android·java·笔记
laocooon52385788635 分钟前
C#二次开发中简单块的定义与应用
android·数据库·c#
似霰1 小时前
传统 Hal 开发笔记5 —— 添加硬件访问服务
android·framework·hal
恋猫de小郭1 小时前
Android 宣布 Runtime 编译速度史诗级提升:在编译时间上优化了 18%
android·前端·flutter
csj501 小时前
安卓基础之《(4)—Activity组件》
android
游戏开发爱好者82 小时前
H5 混合应用加密 Web 资源暴露到 IPA 层防护的完整技术方案
android·前端·ios·小程序·uni-app·iphone·webview
2501_915106322 小时前
最新版本iOS系统设备管理功能全面指南
android·macos·ios·小程序·uni-app·cocoa·iphone
走在路上的菜鸟2 小时前
Android学Dart学习笔记第十四节 库和导库
android·笔记·学习·flutter
姜西西_3 小时前
自动化测试框架pytest之fixture
android·java·pytest