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 = "另一个按钮")
            }
        }       
    }
}

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

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 的所有 EnterTransition 和 ExitTransition 函数之外,AnimatedContent 还提供了 `slideIntoContainer` 和 `slideOutOfContainer`。这些是 slideInHorizontally/Vertically 和 slideOutHorizontally/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 列表

相关推荐
我命由我123452 天前
Android 广播 - 组件通信
android·java·开发语言·android studio·android jetpack·android-studio·android runtime
alexhilton3 天前
为什么你的 LazyColumn 仍然卡顿
android·kotlin·android jetpack
hai_android3 天前
Android 嵌套滑动冲突:内部拦截 vs 外部拦截实战
android·android studio·android jetpack
alexhilton7 天前
模块化Jetpack Compose架构
android·kotlin·android jetpack
hai_android7 天前
InputStage 责任链核心执行流程
android·android studio·android jetpack
传奇开心果编程9 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程9 天前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程10 天前
【现代声明式UI学与练】第9课 性能优化——渲染优化、列表优化、内存优化、启动优化
学习·flutter·react native·ui·性能优化·swiftui·android jetpack
天空之城--12 天前
Android一周动态:Android 18首次官宣、Compose Material3 1.4转正(5趋势+5资讯)
android·人工智能·flutter·架构·android jetpack
传奇开心果编程12 天前
【现代声明式UI学与练】第1课 从命令式UI到声明式UI
学习·flutter·ui·swiftui·react·android jetpack