Jetpack Compose 动画 (动画修饰符和可组合项)

内置动画可组合项

使用 AnimatedVisibility 为出现和消失添加动画效果

ts 复制代码
@Composable
private fun example1() {
  val visible = remember { mutableStateOf(false) }
  Column {
      customTitle(title = "内置动画可组合项")

      Button(modifier = Modifier.padding(start = 15.dp), onClick = { visible.value = !visible.value }) {
          Text(text = if (visible.value) "隐藏另一个按钮" else "显示另一个按钮")
      }
      AnimatedVisibility(visible.value) {
          Button(modifier = Modifier.padding(start = 15.dp), onClick = { }) {
              Text(text = "另一个按钮")
          }
      }
  }
}

默认情况下,内容以淡入和扩大的方式出现,以淡出和缩小的方式消失。您可以通过指定 `EnterTransition``ExitTransition` 来自定义这种过渡效果。

ts 复制代码
@Composable
private fun example2() {
    val visible = remember { mutableStateOf(false) }
    Column {
        customTitle(title = "指定 EnterTransition 和 ExitTransition 来自定义过渡效果")

        Button(modifier = Modifier.padding(start = 15.dp), onClick = { visible.value = !visible.value }) {
            Text(text = if (visible.value) "隐藏另一个按钮" else "显示另一个按钮")
        }
        AnimatedVisibility(visible = visible.value,
            enter = expandVertically(),
            exit = shrinkVertically()) {
            Button(modifier = Modifier.padding(start = 15.dp), onClick = { }) {
                Text(text = "另一个按钮")
            }
        }       
    }
}

如上面的示例所示,您可以使用 + 运算符组合多个 EnterTransitionExitTransition 对象,并且每个对象都接受可选参数以自定义其行为。如需了解详情,请参阅相关参考资料。

EnterTransition ExitTransition
fadeIn fadeOut
slideIn slideOut
slideInHorizontally slideOutHorizontally
slideInVertically slideOutVertically
scaleIn scaleOut
expandIn shrinkOut
expandHorizontally shrinkHorizontally
expandVertically shrinkVertically

AnimatedVisibility 还提供了接受 MutableTransitionState 的变体。这样,只要将 AnimatedVisibility 添加到组合树中,您就可以立即触发动画。该属性还有助于观察动画状态。

ts 复制代码
@Composable
private fun example3() {
    val state = remember { MutableTransitionState(false) }
    Column {
        Button(modifier = Modifier.padding(start = 15.dp), enabled = state.isIdle, onClick = { state.targetState = !state.currentState }) {
            Text(text = when {
                state.isIdle && state.currentState -> "隐藏另一个按钮"
                !state.isIdle && state.currentState -> "正在隐藏..."
                state.isIdle && !state.currentState -> "显示另一个按钮"
                else -> "正在显示..."
            })
        }
        AnimatedVisibility(visibleState  = state) {
            Button(modifier = Modifier.padding(start = 15.dp), onClick = { }) {
                Text(text = "另一个按钮")
            }
        }     
    }
}

为子项添加进入和退出动画效果

AnimatedVisibility 中的内容(直接或间接子项)可以使用 `animateEnterExit` 修饰符为每个子项指定不同的动画行为。其中每个子项的视觉效果均由 AnimatedVisibility 可组合项中指定的动画与子项自己的进入和退出动画构成。

ts 复制代码
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun example4() {
    val visible = remember { mutableStateOf(false) }
    Column {
        customTitle(title = "为子项添加进入和退出动画效果")
        Button(modifier = Modifier.padding(start = 15.dp), onClick = { visible.value = !visible.value }) {
            Text(text = if (visible.value) "隐藏" else "显示")
        }
        AnimatedVisibility(
            visible = visible.value,
            enter = fadeIn(),
            exit = fadeOut()
        ) {
            // Fade in/out the background and the foreground.
            Box(
                Modifier
                    .fillMaxSize()
                    .background(Color.DarkGray)) {
                Box(
                    Modifier
                        .align(Alignment.Center)
                        .animateEnterExit(
                            // Slide in/out the inner box.
                            enter = slideInVertically(),
                            exit = slideOutVertically()
                        )
                        .sizeIn(minWidth = 256.dp, minHeight = 64.dp)
                        .background(Color.Red)
                ) {
                    // Content of the notification...
                }
            }
        }
    }
}

添加自定义动画

如果您想在内置进入和退出动画之外添加自定义动画效果,请通过 AnimatedVisibility 的内容 lambda 内的 transition 属性访问底层 Transition 实例。添加到 Transition 实例的所有动画状态都将与 AnimatedVisibility 的进入和退出动画同时运行。AnimatedVisibility 会等到 Transition 中的所有动画都完成后再移除其内容。对于独立于 Transition(例如使用 animate*AsState)创建的退出动画,AnimatedVisibility 将无法解释这些动画,因此可能会在完成之前移除内容可组合项。

ts 复制代码
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun example5() {
    val visible = remember { mutableStateOf(false) }
    Column {
        customTitle(title = "添加自定义动画")
        Button(modifier = Modifier.padding(start = 15.dp), onClick = { visible.value = !visible.value }) {
            Text(text = if (visible.value) "隐藏" else "显示")
        }
        AnimatedVisibility(
            visible = visible.value,
            enter = fadeIn(),
            exit = fadeOut()
        ) { // this: AnimatedVisibilityScope
            // Use AnimatedVisibilityScope#transition to add a custom animation
            // to the AnimatedVisibility.
            val background = transition.animateColor(label = "color") { state ->
                if (state == EnterExitState.Visible) Color.Blue else Color.Red
            }
            Box(modifier = Modifier.size(200.dp).background(background.value))
        }    
    }
}

使用 AnimatedContent 根据目标状态添加动画效果

`AnimatedContent` 可组合项会在内容根据目标状态发生变化时,为内容添加动画效果。

kotlin 复制代码
@Composable
private fun example6() {
    val count = remember { mutableStateOf(0) }
    Column {
        customTitle(title = "使用 AnimatedContent 根据目标状态添加动画效果")
        Button(modifier = Modifier.padding(start = 15.dp), onClick = { count.value++ }) {
            Text(text = "自增")
        }
        AnimatedContent(targetState = count.value, label = "AnimatedContent") { targetCount ->
            // Make sure to use `targetCount`, not `count`.
            Text(text = "计数: $targetCount",  style = TextStyle(fontSize = 20.sp), modifier = Modifier.padding(all = 15.dp))
        }
        CodeView(assetUrl = "animation/animationModifierComposable/code5.txt", modifier = Modifier.padding(top = 10.dp))
    }
}

EnterTransition 定义了目标内容应如何显示,ExitTransition 则定义了初始内容应如何消失。除了可用于 AnimatedVisibility 的所有 EnterTransitionExitTransition 函数之外,AnimatedContent 还提供了 `slideIntoContainer``slideOutOfContainer`。这些是 slideInHorizontally/VerticallyslideOutHorizontally/Vertically 的便捷替代方案,它们可根据初始内容的大小和 AnimatedContent 内容的目标内容来计算滑动距离。

`SizeTransform` 定义了大小应如何在初始内容与目标内容之间添加动画效果。在创建动画时,您可以访问初始大小和目标大小。SizeTransform 还可控制在动画播放期间是否应将内容裁剪为组件大小。

ts 复制代码
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun example7() {
    val expanded = remember { mutableStateOf(false) }
    Column {
        customTitle(title = "使用 AnimatedContent 根据目标状态添加动画效果")
        Surface(
            color = MaterialTheme.colorScheme.primary,
            onClick = { expanded.value = !expanded.value }
        ) {
            AnimatedContent(
                targetState = expanded.value,
                transitionSpec = {
                    fadeIn(animationSpec = tween(300, 150)) with
                            fadeOut(animationSpec = tween(300)) using
                            SizeTransform { initialSize, targetSize ->
                                if (targetState) {
                                    keyframes {
                                        // Expand horizontally first.
                                        IntSize(targetSize.width, initialSize.height) at 300
                                        durationMillis = 600
                                    }
                                } else {
                                    keyframes {
                                        // Shrink vertically first.
                                        IntSize(initialSize.width, targetSize.height) at 300
                                        durationMillis = 600
                                    }
                                }
                            }
                },
                label = "AnimatedContent"
            ) { targetExpanded ->
                if (targetExpanded) {
                    Text(text = "SizeTransform 定义了大小应如何在初始内容与目标内容之间添加动画效果。在创建动画时,您可以访问初始大小和目标大小。SizeTransform 还可控制在动画播放期间是否应将内容裁剪为组件大小",
                        modifier = Modifier.size(300.dp).padding(all = 5.dp))
                } else {
                    Icon(imageVector = Icons.Filled.Call, contentDescription = "icon",
                        modifier = Modifier.size(50.dp).padding(all = 5.dp))
                }
            }
        }
    }
}

上一篇 Jetpack Compose 列表

相关推荐
眸生5 小时前
基于NeteaseCloudMusicApi的音乐app 支持 DeepSeek 自然语言找歌、批量导入歌单、下载音乐转换成MP3,下载歌词
android·python·kotlin·android studio·音频·fastapi·android jetpack
李斯维2 天前
Jetpack 可观察数据容器 LiveData 的入门与基础使用
android·android jetpack
alexhilton2 天前
车载系统中的可扩展UI:从UI嵌入到系统窗口编排
android·kotlin·android jetpack
帅次3 天前
Android 17 开发者实战:核心更新与应用场景落地指南
android·java·ios·android studio·iphone·android jetpack·webview
我命由我123454 天前
Bugly - Bugly 基本使用( App 质量追踪平台)
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
帅次5 天前
Jetpack Compose 动画实战:animateFloatAsState、AnimatedVisibility 与 graphicsLayer 避坑
android·kotlin·gradle·android jetpack
帅次5 天前
Jetpack Compose 焦点与键盘:FocusRequester、imePadding 与 BringIntoView 实战
android·android studio·android jetpack·android runtime
黄林晴6 天前
Compose 架构大升级,终于支持列表项独立 ViewModel 了!
android·android jetpack
我命由我123457 天前
Dart - 数字类型、布尔类型、列表类型
android·开发语言·flutter·ios·uni-app·android jetpack·移动端
alexhilton10 天前
面向Android开发者的Google I/O 2026
android·kotlin·android jetpack