Объединение анимаций

Последнее обновление: 13.04.2024

Иногда возникает необходимость запустить не одну, а сразу несколько анимаций. Для этой цели в Jetpack Compose применяется Transition, к которому можно добавить несколько дочерних анимаций и запустить их одновременно.

Для создания объекта

который по Несколько анимаций могут запускаться параллельно на основе одного целевого состояния с помощью функции updateTransition(). Эта функция передает целевое состояние и возвращает объект Transition, к которому можно добавить несколько дочерних анимаций.

Функция updateTransition() двольно проста:

@Composable
public fun <T> updateTransition(
    targetState: T,
    label: String? = null
): Transition<T>

В качестве обязательного параметра targetState функции надо передать целевое состояние. Когда целевое состояние изменится, объект Transition запустит все дочерние анимации одновременно. Также может быть передан необязательный параметр label, который можно использовать для идентификации перехода в Animation Inspector в Android Studio.

Класс Transition включает набор функций, которые используются для добавления анимаций. Эти функции называются по шаблону animate[Тип], где [Тип] представляет тип анимируемого значения, например, animateFloat(), animateDp() и animateColor(). Синтаксис этих функций следующий:

val myAnimation by transition.animate[Тип](

    transitionSpec = {

        // определение анимации (с помощью функций tween, spring и т.д.)
    }
) { state ->

    // код вычисления состояния, к которому надо перейти
}

Рассмотмри простейший пример применения переходов Transition:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val boxHeight = 150
            val startOffset = 10
            val endOffset = LocalConfiguration.current.screenHeightDp - boxHeight
            var boxState by remember { mutableStateOf(startOffset)}
            val transition = updateTransition(targetState = boxState)

            // определяем анимацию цвета
            val animatedColor: Color by transition.animateColor(
                transitionSpec = { tween(4000)} // длительность анимации - 4 секунды
            ) {
                state -> if(state == endOffset) Color.Red else Color.DarkGray
            }

            // определяем анимацию отступов
            val animatedOffset: Dp by transition.animateDp(
                transitionSpec = {tween(4000)}
            ) { state -> state.dp  }

            Row(Modifier.fillMaxSize()) {
                Button({boxState = if (boxState==startOffset) endOffset else startOffset },
                    Modifier.padding(10.dp)) {
                    Text("Start", fontSize = 22.sp)
                }
                Box(Modifier.padding(top=animatedOffset).size(boxHeight.dp).background(animatedColor))
            }
        }
    }
}

Здесь мы объединяем две анимации - анимацию цвета и анимацию по Dp (анимируем отступ сверху компонента Box)

Объединение анимаций и Transition в Jetpack Compose на Kotlin в Android

Вначале определяем переменные для вычисления состояния компонента Box:

val boxHeight = 150
val startOffset = 10
val endOffset = LocalConfiguration.current.screenHeightDp - boxHeight

Переменная boxHeight определяет высоту компонента Box. Переменная startOffset представляет начальное смещение сверху. endOffset представляет конечное смещение сверху, которое зависит от свойства LocalConfiguration.current.screenHeightDp. То есть фактически это самый низ экрана.

Далее определяется состояние компонента Box, которое будет отслеживаться для запуска анимации:

var boxState by remember { mutableStateOf(var boxState by remember { mutableStateOf(startOffset)})}

По умолчанию это состояние равно startOffset.

Дальше идет создание объекта Transition:

val transition = updateTransition(targetState = boxState)

Объект Transtion будет отслеживать изменение состояния в boxState. И затем для этого объекта определяем анимации. Сначала определяется анимация цвета с помощью метода animateColor объекта Transition:

val animatedColor: Color by transition.animateColor(
    transitionSpec = { tween(4000)} // длительность анимации - 4 секунды
) {
    state -> if(state == endOffset) Color.Red else Color.DarkGray
}

Прежде всего через параметр transitionSpec задаем настройки анимации. Здесь с помощью вызова функции tween() устанавливаем время анимации - 4 секунды.

В фигурных скобках в лямбда-выражение через параметр state передается текущее состояние. state - это то состояние, которое отслеживает объект Transition и в данном случае это текущее значение boxState. В лямбда-выражении возвращаем значение, которое будет применяться в качестве нового состояния. И в итоге для перехода от текущего к новому состоянию и будет выполняться анимация. В данном случае пусть, если компонент Box прошел около половину пути, то есть соблюдается условие state == endOffset, то Box получает красный цвет, иначе получает темно-серый цвет.

Затем определяется анимация отступов:

val animatedOffset: Dp by transition.animateDp(
    transitionSpec = {tween(4000)}
) { state -> state.dp  }

В нашем случае boxState хранит одно из значение - startOffset или endOffset, который представляют отступ. В данном случае просто преобразуем значение в объект типа Dp и возвращаем его.

Для запуска анимации определена кнопка, которая переключает значение в boxState со startOffset на endOffset и обратно:

Button({boxState = if (boxState==startOffset) endOffset else startOffset },

И в конце определен собственно тот компонент Box, к которому и применяются анимации:

Box(Modifier.padding(top=animatedOffset).size(boxHeight.dp).background(animatedColor))

Его модификатору padding() для параметра top передается результат функции transition.animateDp(), а в модификатор background() - результат функции transition.animateColor

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850