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
внутри лямбда-выражения content для 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 использует это значение в качестве ключа для определения отображаемого в данный момент содержимого.
По умолчанию исходное содержимое плавно исчезает, а затем появляется целевое (это поведение называется плавным переходом ). Вы можете настроить это поведение анимации, указав объект ContentTransform
в параметре transitionSpec
. Вы можете создать ContentTransform
, объединив EnterTransition
с ExitTransition
с помощью инфиксной функции with
. Вы можете применить SizeTransform
к ContentTransform
, присоединив его с помощью инфиксной функции using
.
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
доступен внутри лямбда-функции content объекта 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 } ) { }
Анимация элементов списка
Если вы хотите анимировать переупорядочивание элементов в списке или сетке Lazy, ознакомьтесь с документацией по анимации элементов макета Lazy .
{% дословно %}Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Анимации, основанные на ценностях
- Анимации в Compose
- Поддержка инструментов анимации {:#tooling}