Анимация

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

Для применения анимации в приложении Jetpack Compose предоставляет специальный API - Animation API. Этот API состоит из классов и функций, которые предоставляют широкие возможности по созданию анимации. Рассмотрим ключевые функции и классы Animation API.

Так, Compose Animation API предоставляет ряд анимаций состояния компонентов. В частности, это функции анимации для значений типов Bounds, Color, Dp, Float, Int, IntOffset, IntSize, Offset, Rect и Size. Подобные функции покрывают большинство потребностей в анимации компонентов.

Подобные функции анимаций используют одно и то же соглашение об именах. В частности, все они называются по шаблону:

animate*AsState

где символ * представляет типо состояния, которое анимируется. Например, если нужно анимировать изменение цвета (например, цвета фона компонента), то применяется функция animateColorAsState(). В реальности, функции передается целевое (конечное) значение, которое должно получить состояние. И функция анимируется переход от текущего значения к целевому значению. Рассмотрим ряд подобных функций.

Анимация Dp. animateDpAsState

Функция animateDpAsState() выполняет анимацию значений Dp, которые могут применяться для установки размеров, отступов и т.д. Она имеет следующие параметры:

@Composable
public fun animateDpAsState(
    targetValue: Dp,
    animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
    label: String = "DpAnimation",
    finishedListener: ((Dp) -> Unit)? = null
): State<Dp>
  • targetValue: значение Dp, к которому надо выполнить переход

  • animationSpec: применяемая анимация в виде объекта AnimationSpec

  • label: название анимации

  • finishedListener: функция, которая выполняется при завершении анимации

Обязательным параметром является только targetValue. Рассмотрим простейший пример, в котором анимируется отступ слева, за счет чего возникает иллюзия движения объекта:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
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.unit.dp
import androidx.compose.ui.unit.sp


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val startOffset = 0   // начальная позиция
            val endOffset = 300   // конечная позиция
            val boxWidth = 150      // ширина компонента

            var boxState by remember { mutableStateOf(startOffset)}

            val offset by animateDpAsState(targetValue = boxState.dp)

            Column(Modifier.fillMaxWidth()) {
                Box(Modifier.padding(start=offset).size(boxWidth.dp).background(Color.DarkGray))

                Button({boxState = if (boxState==startOffset) {endOffset} else {startOffset} },
                    Modifier.padding(10.dp)) {
                    Text("Move", fontSize = 25.sp)
                }
            }
        }
    }
}

В данном случае по нажатию на кнопку происходит перемещение компонента с помощью анимации значений Dp:

animateDpAsState и анимация Dp в Jetpack Compose на Kotlin в Android

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

val startOffset = 0   // начальная позиция
val endOffset = 400   // конечная позиция
val boxWidth = 200      // ширина компонента

То есть наш Box имеет ширину boxWidth и будет перемещаться от позиции startOffset до endOffset.

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

var boxState by remember { mutableStateOf(startOffset)}

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

Затем определяем анимацию позиции Box с помощью функции animateDpAsState()

val offset: Dp by animateDpAsState(targetValue = boxState.dp)

Параметр targetValue на основании boxState определяет позицию, к которой надо выполнить переход. В данном случае все просто - boxState будет хранить отступ, и здесь мы его преобразуем в значение Dp.

Функция animateDpAsState() возвращает значение типа State<Dp>, и с помощью оператора by из него извлекаем непосредственно значение Dp и передаем его переменной offset.

Далее используем значение offset, полученное из функции animateDpAsState(), для установки отступа от начала контейнера для компонента Box:

Box(Modifier.padding(start=offset)      // устанавливаем отступ
        .size(boxWidth.dp)
        .background(Color.DarkGray))

И для запуска анимации на кнопку Move вешаем обработчик нажатия, который переключает значение boxState со startOffset на endOffset и обратно.

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

То есть при нажатии на кнопку изменится значение boxState. Поскольку функция animateDpAsState зависит от boxState и на его основании устанавливает целевое значение, к которому надо перейти, то будет выполняться анимация для перехода к новому целевому значению.

Подобным образом мы можем анимировать любые параметры на основе Dp, не только отсутпы. Однако чуть улучшим предыдущий код. Так, выше значение endOffset взято случайным образом. Теперь сделаем движение Box до конца экрана:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
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.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.platform.LocalConfiguration


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val boxWidth = 150      // ширина компонента
            val startOffset = 0   // начальная позиция
            val endOffset = LocalConfiguration.current.screenWidthDp - boxWidth // конечная позиция

            var boxState by remember { mutableStateOf(startOffset)}

            val offset by animateDpAsState(targetValue = boxState.dp)

            Column(Modifier.fillMaxWidth()) {
                Box(Modifier.padding(start=offset).size(boxWidth.dp).background(Color.DarkGray))

                Button({boxState = if (boxState==startOffset) {endOffset} else {startOffset} },
                    Modifier.padding(10.dp)) {
                    Text("Move", fontSize = 25.sp)
                }
            }
        }
    }
}
animateDpAsState и анимация движения компонента в Jetpack Compose на Kotlin в Android

Теперь для вычисления конечной позиции применяется свойство LocalConfiguration.current.screenWidthDp, которое возвращает ширину экрана:

val endOffset = LocalConfiguration.current.screenWidthDp - boxState

Кроме того, при вычислении конечной позиции учитываем ширину компонента, чтобы он не вылез за экран.

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