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`\] 可组合项会在内容根据目标状态发生变化时,为内容添加动画效果。 ![petal_20240412_100400_20240412_100437.gif](https://file.jishuzhan.net/article/1779826929759686658/fb168c9c30d1a4c503d5e930193e481a.webp) ```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` 的所有 `EnterTransition` 和 `ExitTransition` 函数之外,`AnimatedContent` 还提供了 \[`slideIntoContainer`\] 和 \[`slideOutOfContainer`\]。这些是 `slideInHorizontally/Vertically` 和 `slideOutHorizontally/Vertically` 的便捷替代方案,它们可根据初始内容的大小和 `AnimatedContent` 内容的目标内容来计算滑动距离。 \[`SizeTransform`\] 定义了大小应如何在初始内容与目标内容之间添加动画效果。在创建动画时,您可以访问初始大小和目标大小。`SizeTransform` 还可控制在动画播放期间是否应将内容裁剪为组件大小。 ![petal_20240412_180550_20240415_151701.gif](https://file.jishuzhan.net/article/1779826929759686658/36e187228e151b28b5167b8c4e667719.webp) ```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 列表](https://juejin.cn/post/7356177721998131210 "https://juejin.cn/post/7356177721998131210")

相关推荐
ljt27249606611 天前
Compose笔记(二十三)--多点触控
笔记·android jetpack
我命由我1234517 天前
Android 解绑服务问题:java.lang.IllegalArgumentException: Service not registered
android·java·开发语言·java-ee·安卓·android jetpack·android-studio
我命由我1234519 天前
MQTT - Android MQTT 编码实战(MQTT 客户端创建、MQTT 客户端事件、MQTT 客户端连接配置、MQTT 客户端主题)
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
前行的小黑炭19 天前
Android LiveData源码分析:为什么他刷新数据比Handler好,能更节省资源,解决内存泄漏的隐患;
android·kotlin·android jetpack
_一条咸鱼_19 天前
深度剖析:Java PriorityQueue 使用原理大揭秘
android·面试·android jetpack
_一条咸鱼_19 天前
揭秘 Java PriorityBlockingQueue:从源码洞悉其使用原理
android·面试·android jetpack
_一条咸鱼_19 天前
深度揭秘:Java LinkedList 源码级使用原理剖析
android·面试·android jetpack
_一条咸鱼_19 天前
深入剖析 Java LinkedBlockingQueue:源码级别的全面解读
android·面试·android jetpack
_一条咸鱼_19 天前
探秘 Java DelayQueue:源码级剖析其使用原理
android·面试·android jetpack
_一条咸鱼_20 天前
揭秘 Java ArrayDeque:从源码到原理的深度剖析
android·面试·android jetpack