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
中缀函数附加SizeTransform
到ContentTransform
。
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}