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
. Чтобы добиться этого, укажите EnterTransition.None
и ExitTransition.None
в компонуемом AnimatedVisibility
.
Добавить пользовательскую анимацию
Если вы хотите добавить пользовательские эффекты анимации помимо встроенных анимаций входа и выхода, получите доступ к базовому экземпляру Transition
через свойство transition
внутри лямбда-выражения содержимого для AnimatedVisibility
. Любые состояния анимации, добавленные к экземпляру 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") } }
Обратите внимание, что вы всегда должны использовать параметр лямбда и отражать его в контенте. API использует это значение как ключ для идентификации контента, который в данный момент отображается.
По умолчанию исходное содержимое постепенно исчезает, а затем постепенно появляется целевое содержимое (такое поведение называется fade through ). Вы можете настроить это поведение анимации, указав объект ContentTransform
для параметра transitionSpec
. Вы можете создать ContentTransform
, объединив EnterTransition
с ExitTransition
, используя функцию with
infix. Вы можете применить 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() 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
определяет, как должно исчезать исходное содержимое. В дополнение ко всем функциям EnterTransition
и ExitTransition
, доступным для AnimatedVisibility
, 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
. Используйте его, чтобы применить EnterAnimation
и ExitAnimation
к каждому из прямых или косвенных потомков по отдельности.
Добавить пользовательскую анимацию
Как и AnimatedVisibility
, поле transition
доступно внутри лямбда-функции content AnimatedContent
. Используйте это для создания пользовательского эффекта анимации, который выполняется одновременно с переходом 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 } ) { }
Анимация элементов списка
Если вы хотите анимировать переупорядочивание элементов в ленивом списке или сетке, ознакомьтесь с документацией по анимации элементов ленивой компоновки .
{% дословно %}Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Ценностно-ориентированная анимация
- Анимации в Compose
- Поддержка инструментов анимации {:#tooling}