0Pricing
Android Academy · Lekcja

Podstawy animate*AsState

Płynnie animuj pojedyncze wartości

Podstawy animate*AsState to bezpłatna lekcja Android Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Android Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Android Academy zawiera 4 lekcji w sumie.

Animowanie pojedynczej wartości

Najprostszym sposobem animowania w Compose jest rodzina animate*AsState. Przekazuje mu Pan/Pani wartość docelową, a w wyniku otrzymuje State, który płynnie animuje się od bieżącej wartości do wartości docelowej za każdym razem, gdy cel się zmieni.

Dla każdego typowego typu istnieje odpowiedni wariant: animateFloatAsState, animateDpAsState, animateColorAsState, animateOffsetAsState i inne.

animateFloatAsState

animateFloatAsState animuje pojedynczą wartość Float. Częstym zastosowaniem jest przezroczystość (alpha). Gdy wartość visible się zmieni, zwracana wartość alpha płynnie przechodzi między 0f a 1f.

Proszę zwrócić uwagę na słowo kluczowe by: pozwala ono odczytywać animowany State<Float> bezpośrednio jako zwykły 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

Rozmiary i przesunięcia interfejsu użytkownika korzystają z Dp, dlatego do animowania wypełnienia, rozmiaru lub pozycji warto użyć animateDpAsState.

W tym przykładzie pole po przełączeniu rośnie z 80dp do 160dp. Ponieważ animowany element Dp przekazujemy do size(), pole płynnie zmienia rozmiar.

@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

Do animowania kolorów służy animateColorAsState. Interpoluje on w przestrzeni kolorów, dzięki czemu przejścia wyglądają płynnie i naturalnie, zamiast migotać przez nietypowe odcienie pośrednie.

W tym przykładzie kropka stanu animuje się między czerwonym a zielonym kolorem na podstawie wartości logicznej.

@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)
    )
}

Dostosowywanie za pomocą animationSpec

Domyślnie te funkcje pomocnicze korzystają z łagodnej sprężyny. Przekazanie animationSpec pozwala kontrolować czas trwania. Do ustalonego czasu trwania z krzywą easing należy użyć tween.

Ten panel przesuwa się przez 400 ms po płynnej krzywej, która najpierw przyspiesza, a następnie zwalnia.

@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 */ }
}

Krzywe easing

Easing określa sposób przyspieszania i zwalniania animacji. Compose udostępnia kilka krzywych:

  • LinearEasing — stała prędkość (mechaniczne wrażenie).
  • FastOutSlowInEasing — domyślna krzywa Material; szybki początek i łagodne zakończenie.
  • LinearOutSlowInEasing — dla elementów pojawiających się na ekranie.
  • FastOutLinearInEasing — dla elementów opuszczających ekran.

Wybór właściwego easing sprawia, że ruch wydaje się zamierzony, a nie zrobotyzowany.

val progress by animateFloatAsState(
    targetValue = if (done) 1f else 0f,
    animationSpec = tween(500, easing = LinearOutSlowInEasing),
    label = "progress"
)

Specyfikacje sprężyn

Zamiast ustalonego czasu trwania można użyć sprężyny, która daje fizyczne i naturalne wrażenie. Sprężynę definiują dampingRatio (określający sprężystość) i stiffness (określający szybkość).

Sprężyny są świetnym rozwiązaniem, ponieważ płynnie dostosowują się nawet wtedy, gdy cel zmieni się w trakcie animacji.

val scale by animateFloatAsState(
    targetValue = if (active) 1.2f else 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioMediumBouncy,
        stiffness = Spring.StiffnessLow
    ),
    label = "scale"
)

Reagowanie na zakończenie

Czasami trzeba wykonać działanie po zakończeniu animacji, na przykład wywołać callback, gdy wartość osiągnie cel. animate*AsState udostępnia do tego celu parametr finishedListener.

val alpha by animateFloatAsState(
    targetValue = if (gone) 0f else 1f,
    label = "alpha",
    finishedListener = { finalValue ->
        if (finalValue == 0f) onFullyHidden()
    }
)

Animowanie wielu właściwości

Można połączyć kilka wywołań animate*AsState, aby jednocześnie animować więcej niż jedną właściwość. Każde z nich śledzi własną wartość, a razem tworzą bogatszy efekt.

W tym przykładzie element chip skaluje się i zmienia kolor po zaznaczeniu.

@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) }
}

Sprężyna w czystym Kotlinie

Aby wyrobić sobie intuicję dotyczącą działania sprężyny, przedstawiamy krótką symulację w czystym Kotlinie. W każdym kroku delikatnie przesuwa ona wartość w kierunku celu, stosując pewne tłumienie — podobnie jak sprężyna Compose dąży do wartości docelowej przez wiele klatek.

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)}")
    }
}

Animowanie Int i Offset

Ta rodzina wykracza poza liczby zmiennoprzecinkowe i kolory. animateIntAsState służy do wartości całkowitych, takich jak docelowa liczba na plakietce, a animateOffsetAsState lub animateIntOffsetAsState pozwala animować pozycję 2D w jednym wywołaniu.

W tym przykładzie element chip przesuwa się po przekątnej dzięki animowaniu 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)
    )
}

Szybkie sprawdzenie

Chce Pan/Pani płynnie animować szerokość pola, mierzoną w Dp, gdy zmienia się wartość logiczna. Który interfejs API będzie najlepszy?

Podsumowanie: animate*AsState

Rodzina animate*AsState jest podstawowym rozwiązaniem do animowania pojedynczej wartości:

  • Należy wybrać wariant odpowiedni dla typu: animateFloatAsState, animateDpAsState, animateColorAsState itd.
  • Należy ustawić targetValue; wynik animuje się w jego kierunku przy każdej zmianie.
  • Ruchem można sterować za pomocą animationSpec, używając tween z easingiem lub spring.
  • Kilka funkcji można połączyć, aby animować wiele właściwości jednocześnie.
  • Parametr finishedListener pozwala reagować na zakończenie animacji.

Następnie: animowanie pojawiania się i znikania treści za pomocą AnimatedVisibility i przejść.

Często zadawane pytania

Czy lekcja „Podstawy animate*AsState” jest bezpłatna?

Tak — pełny tekst „Podstawy animate*AsState” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Android Academy, przejdź na CoddyKit PRO. Kurs Android Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Podstawy animate*AsState”?

Płynnie animuj pojedyncze wartości Ćwiczysz Android Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Android Academy?

Nie wymagamy żadnego doświadczenia. Android Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Podstawy animate*AsState”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Android Academy?

Tak. Każda lekcja Android Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Dlaczego animacje mają znaczenie
  2. Podstawy animate*AsState
  3. AnimatedVisibility i przejścia
  4. Animacje gestów i sprężynowe
← Powrót do Android Academy