Модификаторы анимации и составные элементы

Compose поставляется со встроенными компонентами и модификаторами для обработки распространенных случаев использования анимации.

Встроенные анимированные составные элементы

Анимируйте появление и исчезновение с помощью AnimatedVisibility

Зеленый составной элемент, показывающий и прячущийся
Рисунок 1. Анимация появления и исчезновения элемента в столбце

Составной элемент 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

Зеленый составной элемент, плавно анимирующий изменение его размера.
Рис. 2. Плавная компонуемая анимация между маленьким и большим размером.

Модификатор 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
       
}

) {
}

Список анимаций элементов

Если вы хотите анимировать переупорядочение элементов внутри ленивого списка или сетки, ознакомьтесь с документацией по анимации элементов ленивого макета .

{% дословно %}

Пока рекомендаций нет.

Попытайтесь в свой аккаунт Google.

{% дословно %}