Compose 提供了内置的可组合项和修饰符,用于处理常见的动画用例。
内置动画可组合项
使用 AnimatedVisibility
实现出现和消失动画

AnimatedVisibility
可组合项可为其内容添加出现和消失动画。
var visible by remember { mutableStateOf(true) } // Animated visibility will eventually remove the item from the composition once the animation has finished. AnimatedVisibility(visible) { // your composable here // ... }
默认情况下,内容通过淡入和展开来出现,通过淡出和收缩来消失。可以通过指定 EnterTransition
和 ExitTransition
来自定义过渡效果。
var visible by remember { mutableStateOf(true) } val density = LocalDensity.current AnimatedVisibility( visible = visible, enter = slideInVertically { // Slide in from 40 dp from the top. with(density) { -40.dp.roundToPx() } } + expandVertically( // Expand from the top. expandFrom = Alignment.Top ) + fadeIn( // Fade in with the initial alpha of 0.3f. initialAlpha = 0.3f ), exit = slideOutVertically() + shrinkVertically() + fadeOut() ) { Text( "Hello", Modifier .fillMaxWidth() .height(200.dp) ) }
如上例所示,您可以使用 +
运算符组合多个 EnterTransition
或 ExitTransition
对象,每个对象都接受可选参数以自定义其行为。如需了解详情,请参阅参考资料。
EnterTransition
和 ExitTransition
示例
AnimatedVisibility
还提供了一个采用 MutableTransitionState
的变体。这使您可以在 AnimatedVisibility
添加到组合树时立即触发动画。它对于观察动画状态也很有用。
// Create a MutableTransitionState<Boolean> for the AnimatedVisibility. val state = remember { MutableTransitionState(false).apply { // Start the animation immediately. targetState = true } } Column { AnimatedVisibility(visibleState = state) { Text(text = "Hello, world!") } // Use the MutableTransitionState to know the current animation state // of the AnimatedVisibility. Text( text = when { state.isIdle && state.currentState -> "Visible" !state.isIdle && state.currentState -> "Disappearing" state.isIdle && !state.currentState -> "Invisible" else -> "Appearing" } ) }
为子级添加进入和退出动画
AnimatedVisibility
中的内容(直接或间接子级)可以使用 animateEnterExit
修饰符,为每个子级指定不同的动画行为。每个子级的视觉效果是 AnimatedVisibility
可组合项中指定的动画与子级自己的进入和退出动画的组合。
var visible by remember { mutableStateOf(true) } AnimatedVisibility( visible = visible, 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
完全不应用动画,以便每个子级可以通过 animateEnterExit
拥有自己的独特动画。要实现此目的,请在 AnimatedVisibility
可组合项中指定 EnterTransition.None
和 ExitTransition.None
。
添加自定义动画
如果您想添加超出内置进入和退出动画的自定义动画效果,可以通过 AnimatedVisibility
的内容 lambda 中的 transition
属性访问底层 Transition
实例。添加到 Transition 实例的任何动画状态将与 AnimatedVisibility
的进入和退出动画同时运行。AnimatedVisibility
会等待 Transition
中的所有动画完成后再移除其内容。对于独立于 Transition
创建的退出动画(例如使用 animate*AsState
),AnimatedVisibility
无法考虑这些动画,因此可能会在它们完成之前移除内容可组合项。
var visible by remember { mutableStateOf(true) } AnimatedVisibility( visible = visible, enter = fadeIn(), exit = fadeOut() ) { // this: AnimatedVisibilityScope // Use AnimatedVisibilityScope#transition to add a custom animation // to the AnimatedVisibility. val background by transition.animateColor(label = "color") { state -> if (state == EnterExitState.Visible) Color.Blue else Color.Gray } Box( modifier = Modifier .size(128.dp) .background(background) ) }
有关 Transition
的详细信息,请参阅 updateTransition。
使用 AnimatedContent
根据目标状态添加动画
AnimatedContent
可组合项会根据目标状态的变化为其内容添加动画效果。
Row { var count by remember { mutableIntStateOf(0) } Button(onClick = { count++ }) { Text("Add") } AnimatedContent( targetState = count, label = "animated content" ) { targetCount -> // Make sure to use `targetCount`, not `count`. Text(text = "Count: $targetCount") } }
请注意,您应该始终使用 lambda 参数并将其反映到内容中。API 使用此值作为键来标识当前显示的内容。
默认情况下,初始内容会淡出,然后目标内容会淡入(此行为称为淡入淡出)。您可以通过将 ContentTransform
对象指定给 transitionSpec
参数来自定义此动画行为。您可以通过使用 with
中缀函数将 EnterTransition
与 ExitTransition
组合来创建 ContentTransform
。您可以通过使用 using
中缀函数将其附加到 ContentTransform
来应用 SizeTransform
。
AnimatedContent( targetState = count, transitionSpec = { // Compare the incoming number with the previous number. if (targetState > initialState) { // If the target number is larger, it slides up and fades in // while the initial (smaller) number slides up and fades out. slideInVertically { height -> height } + fadeIn() togetherWith slideOutVertically { height -> -height } + fadeOut() } else { // If the target number is smaller, it slides down and fades in // while the initial number slides down and fades out. slideInVertically { height -> -height } + fadeIn() togetherWith slideOutVertically { height -> height } + fadeOut() }.using( // Disable clipping since the faded slide-in/out should // be displayed out of bounds. SizeTransform(clip = false) ) }, label = "animated content" ) { targetCount -> Text(text = "$targetCount") }
EnterTransition
定义了目标内容应如何出现,而 ExitTransition
定义了初始内容应如何消失。除了 AnimatedVisibility
可用的所有 EnterTransition
和 ExitTransition
函数之外,AnimatedContent
还提供了 slideIntoContainer
和 slideOutOfContainer
。这些是 slideInHorizontally/Vertically
和 slideOutHorizontally/Vertically
的便捷替代方案,它们根据 AnimatedContent
内容的初始内容和目标内容的尺寸计算滑动距离。
SizeTransform
定义了尺寸应如何在初始内容和目标内容之间进行动画处理。在创建动画时,您可以同时访问初始尺寸和目标尺寸。SizeTransform
还控制动画期间内容是否应剪裁到组件尺寸。
var expanded by remember { mutableStateOf(false) } Surface( color = MaterialTheme.colorScheme.primary, onClick = { expanded = !expanded } ) { AnimatedContent( targetState = expanded, transitionSpec = { fadeIn(animationSpec = tween(150, 150)) togetherWith fadeOut(animationSpec = tween(150)) using SizeTransform { initialSize, targetSize -> if (targetState) { keyframes { // Expand horizontally first. IntSize(targetSize.width, initialSize.height) at 150 durationMillis = 300 } } else { keyframes { // Shrink vertically first. IntSize(initialSize.width, targetSize.height) at 150 durationMillis = 300 } } } }, label = "size transform" ) { targetExpanded -> if (targetExpanded) { Expanded() } else { ContentIcon() } } }
为子级添加进入和退出过渡动画
就像 AnimatedVisibility
一样,animateEnterExit
修饰符在 AnimatedContent
的内容 lambda 中可用。使用此修饰符可分别为每个直接或间接子级应用 EnterAnimation
和 ExitAnimation
。
添加自定义动画
就像 AnimatedVisibility
一样,transition
字段在 AnimatedContent
的内容 lambda 中可用。使用此字段创建与 AnimatedContent
过渡同时运行的自定义动画效果。有关详细信息,请参阅 updateTransition。
使用 Crossfade
在两个布局之间交叉淡入淡出
Crossfade
使用交叉淡入淡出动画在两个布局之间进行切换。通过切换传递给 current
参数的值,内容会以交叉淡入淡出动画进行切换。
var currentPage by remember { mutableStateOf("A") } Crossfade(targetState = currentPage, label = "cross fade") { screen -> when (screen) { "A" -> Text("Page A") "B" -> Text("Page B") } }
内置动画修饰符
使用 animateContentSize
为可组合项大小变化添加动画

animateContentSize
修饰符可为尺寸变化添加动画。
var expanded by remember { mutableStateOf(false) } Box( modifier = Modifier .background(colorBlue) .animateContentSize() .height(if (expanded) 400.dp else 200.dp) .fillMaxWidth() .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { expanded = !expanded } ) { }
列表项动画
如果您希望在 Lazy 列表或网格中为项目重新排序添加动画效果,请查看Lazy 布局项目动画文档。
为您推荐
- 注意:当 JavaScript 关闭时显示链接文本
- 基于值的动画
- Compose 中的动画
- 动画工具支持 {:#tooling}