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 } ) { }
Анимация элементов списка
Если вы хотите анимировать переупорядочивание элементов в ленивом списке или сетке, ознакомьтесь с документацией по анимации элементов ленивой компоновки .
{% дословно %}Рекомендуем
Animations in Compose
Jetpack Compose is Android's recommended modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.