Compose 內建可組合函式和修飾符,可以處理常見的動畫用途。
內建動畫可組合項
使用 AnimatedVisibility
以動畫出現和消失
![顯示並隱藏的綠色可組合函式](https://developer.android.google.cn/static/develop/ui/compose/images/animations/animated_visibility_column.gif?authuser=3&hl=zh-tw)
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
,請參閱 updateConversion。
使用 AnimatedContent
根據目標狀態建立動畫
AnimatedContent
可組合項可根據目標狀態產生變動時,為內容建立動畫效果。
Row { var count by remember { mutableStateOf(0) } Button(onClick = { count++ }) { Text("Add") } AnimatedContent(targetState = count) { targetCount -> // Make sure to use `targetCount`, not `count`. Text(text = "Count: $targetCount") } }
請注意,您應一律使用 lambda 參數,並將其反映至內容當中。API 會使用這個值做為索引鍵來識別目前顯示的內容。
根據預設,初始內容會淡出,然後目標內容淡入 (這個行為稱為淡出淡入切換)。只要將 ContentTransform
物件指定至 transitionSpec
參數,即可自訂這個動畫的行為。您可以使用 with
infix 函式結合 EnterTransition
與 ExitTransition
來建立 ContentTransform
。可以將 SizeTransform
套用至 ContentTransform
,做法是將它加至 using
infix 函式裡。
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() with 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() with slideOutVertically { height -> height } + fadeOut() }.using( // Disable clipping since the faded slide-in/out should // be displayed out of bounds. SizeTransform(clip = false) ) } ) { 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)) with 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 } } } } ) { targetExpanded -> if (targetExpanded) { Expanded() } else { ContentIcon() } } }
為子項進入和離開轉場的動畫呈現
與 AnimatedVisibility
一樣,AnimatedContent
的內容 lambda 中具有animateEnterExit
修飾元。使用此方法將 EnterAnimation
和 ExitAnimation
分別套用至每個直接或間接的子項中。
新增自訂動畫
與 AnimatedVisibility
一樣,transition
欄位可在 AnimatedContent
的內容 lambda 中使用。只要使用這個元件,即可建立可與 AnimatedContent
轉場同時執行的自訂動畫效果。詳情請參閱「updateTransition」。
使用 Crossfade
為兩個版面配置建立動畫效果
Crossfade
可在兩個版面配置之間建立交叉漸變的動畫效果。透過切換傳遞至 current
參數的值,讓系統以交叉漸變的動畫效果切換顯示內容。
var currentPage by remember { mutableStateOf("A") } Crossfade(targetState = currentPage) { screen -> when (screen) { "A" -> Text("Page A") "B" -> Text("Page B") } }
內建動畫修飾符
使用 animateContentSize
以動畫呈現可組合函式大小變更
![綠色可組合項為大小變更建立動畫流暢的動畫。](https://developer.android.google.cn/static/develop/ui/compose/images/animations/animated_content_size.gif?authuser=3&hl=zh-tw)
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}