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 использует это значение в качестве ключа для идентификации отображаемого в данный момент контента.
По умолчанию исходное содержимое исчезает, а затем появляется целевое содержимое (это поведение называется сквозным исчезновением ). Вы можете настроить это поведение анимации, указав объект 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
доступно внутри лямбда-выражения содержимого 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}