Основы animate*AsState
Плавно анимируйте отдельные значения
«Основы animate*AsState» — бесплатный урок Android Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.
Анимация одного значения
Самый простой способ анимировать в Compose — использовать семейство animate*AsState. Вы передаёте ему целевое значение, а он возвращает State, которое плавно изменяется от текущего значения к целевому при каждом изменении цели.
Для каждого распространённого типа есть свой вариант: animateFloatAsState, animateDpAsState, animateColorAsState, animateOffsetAsState и другие.
animateFloatAsState
animateFloatAsState анимирует одно значение типа Float. Частый вариант применения — прозрачность (альфа-канал). Когда значение visible меняется, возвращаемая альфа плавно переходит между 0f и 1f.
Обратите внимание на ключевое слово by: оно позволяет напрямую читать анимированное State<Float> как обычный Float.
@Composable
fun FadingLabel(visible: Boolean) {
val alpha by animateFloatAsState(
targetValue = if (visible) 1f else 0f,
label = "alpha"
)
Text(
"Hello",
modifier = Modifier.graphicsLayer { this.alpha = alpha }
)
}animateDpAsState
Размеры и смещения интерфейса используют Dp, поэтому для анимации внутренних отступов, размера или положения применяйте animateDpAsState.
Здесь при переключении прямоугольная область увеличивается с 80dp до 160dp. Поскольку анимированное значение Dp передаётся в size(), размер области плавно изменяется.
@Composable
fun GrowingBox(big: Boolean) {
val size by animateDpAsState(
targetValue = if (big) 160.dp else 80.dp,
label = "size"
)
Box(
Modifier
.size(size)
.background(Color.Blue)
)
}animateColorAsState
Для анимации цветов используйте animateColorAsState. Он выполняет интерполяцию в цветовом пространстве, поэтому переходы выглядят плавно и естественно, без мерцания необычными промежуточными оттенками.
Здесь индикатор состояния анимируется между красным и зелёным в зависимости от логического значения.
@Composable
fun StatusDot(online: Boolean) {
val color by animateColorAsState(
targetValue = if (online) Color(0xFF4CAF50) else Color(0xFFF44336),
label = "status-color"
)
Box(
Modifier
.size(16.dp)
.clip(CircleShape)
.background(color)
)
}Настройка с помощью animationSpec
По умолчанию эти вспомогательные функции используют мягкую пружину. Передайте animationSpec, чтобы управлять временем анимации. Используйте tween, чтобы задать фиксированную длительность и кривую плавности.
Эта панель скользит в течение 400 мс по плавной кривой с ускорением, за которым следует замедление.
@Composable
fun SlidingPanel(open: Boolean) {
val offset by animateDpAsState(
targetValue = if (open) 0.dp else 250.dp,
animationSpec = tween(
durationMillis = 400,
easing = FastOutSlowInEasing
),
label = "offset"
)
Box(Modifier.offset(x = offset)) { /* content */ }
}Кривые плавности
Кривая плавности определяет, как анимация ускоряется и замедляется. Compose предоставляет несколько вариантов:
LinearEasing— постоянная скорость (механическое ощущение).FastOutSlowInEasing— стандартный вариант Material: быстрое начало и мягкое завершение.LinearOutSlowInEasing— для элементов, появляющихся на экране.FastOutLinearInEasing— для элементов, исчезающих с экрана.
Правильно выбранная кривая плавности делает движение осмысленным, а не роботизированным.
val progress by animateFloatAsState(
targetValue = if (done) 1f else 0f,
animationSpec = tween(500, easing = LinearOutSlowInEasing),
label = "progress"
)Параметры пружины
Вместо фиксированной длительности можно использовать пружину, которая ощущается физичной и естественной. Пружина определяется параметрами dampingRatio (степень упругости) и stiffness (скорость).
Пружины особенно удобны тем, что плавно адаптируются даже при изменении цели во время анимации.
val scale by animateFloatAsState(
targetValue = if (active) 1.2f else 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
),
label = "scale"
)Реакция на завершение
Иногда после завершения анимации нужно выполнить действие, например вызвать обратный вызов, когда значение достигнет цели. Для этого animate*AsState принимает finishedListener.
val alpha by animateFloatAsState(
targetValue = if (gone) 0f else 1f,
label = "alpha",
finishedListener = { finalValue ->
if (finalValue == 0f) onFullyHidden()
}
)Анимация нескольких свойств
Вы можете объединить несколько вызовов animate*AsState, чтобы одновременно анимировать несколько свойств. Каждый вызов отслеживает собственное значение, а вместе они создают более выразительный эффект.
Здесь чип одновременно масштабируется и меняет цвет при выборе.
@Composable
fun SelectableChip(selected: Boolean, text: String) {
val scale by animateFloatAsState(if (selected) 1.1f else 1f, label = "s")
val bg by animateColorAsState(
if (selected) Color(0xFF3F51B5) else Color.LightGray,
label = "bg"
)
Box(
Modifier
.graphicsLayer { scaleX = scale; scaleY = scale }
.clip(RoundedCornerShape(16.dp))
.background(bg)
.padding(horizontal = 12.dp, vertical = 6.dp)
) { Text(text) }
}Пружина на чистом Kotlin
Чтобы понять, что именно «делает» пружина, рассмотрим небольшую симуляцию на чистом Kotlin. На каждом шаге она приближает значение к цели с некоторым затуханием — примерно так же, как пружина Compose приближается к целевому значению за множество кадров.
fun main() {
var value = 0f
val target = 100f
var velocity = 0f
val stiffness = 0.1f
val damping = 0.6f
repeat(6) { step ->
val force = (target - value) * stiffness
velocity = (velocity + force) * damping
value += velocity
println("step $step -> ${"%.1f".format(value)}")
}
}Анимация Int и Offset
Семейство функций поддерживает не только числа с плавающей точкой и цвета. Используйте animateIntAsState для целочисленных значений, например целевого количества элементов на значке, а animateOffsetAsState или animateIntOffsetAsState — для анимации двумерного положения одним вызовом.
Здесь чип перемещается по диагонали благодаря анимации IntOffset.
@Composable
fun SlidingChip(moved: Boolean) {
val pos by animateIntOffsetAsState(
targetValue = if (moved) IntOffset(120, 60) else IntOffset.Zero,
label = "pos"
)
Box(
Modifier
.offset { pos }
.size(40.dp)
.background(Color.Magenta)
)
}Быстрая проверка
Вы хотите плавно анимировать ширину прямоугольной области, измеряемую в Dp, при изменении логического значения. Какой API лучше всего подходит?
Итоги: animate*AsState
Семейство animate*AsState — основной инструмент для анимации одного значения:
- Выберите вариант нужного типа:
animateFloatAsState,animateDpAsState,animateColorAsStateи т. д. - Задайте
targetValue; результат будет анимированно приближаться к нему при каждом изменении. - Настройте движение с помощью
animationSpec, используяtweenс кривой плавности илиspring. - Объединяйте несколько вызовов для одновременной анимации нескольких свойств.
- Используйте
finishedListener, чтобы реагировать на завершение анимации.
Далее: анимация появления и исчезновения содержимого с помощью AnimatedVisibility и переходов.
Часто задаваемые вопросы
Урок «Основы animate*AsState» бесплатный?
Да — полный текст урока «Основы animate*AsState» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.
Чему я научусь в уроке «Основы animate*AsState»?
Плавно анимируйте отдельные значения Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Основы animate*AsState»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Зачем нужны анимации
- Основы animate*AsState
- AnimatedVisibility и переходы
- Анимации жестов и пружин