Android Academy · Lezione

Fondamenti di animate*AsState

Animi fluidamente valori singoli

Lezione 2 di 413 passaggi

Fondamenti di animate*AsState è una lezione Android Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Android Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Android Academy include 4 lezioni in totale.

Animare un singolo valore

Il modo più semplice per animare in Compose consiste nell'usare la famiglia animate*AsState. Le si fornisce un valore obiettivo e restituisce uno State che anima gradualmente il valore corrente verso quello obiettivo ogni volta che quest'ultimo cambia.

Esiste una variante per ogni tipo comune: animateFloatAsState, animateDpAsState, animateColorAsState, animateOffsetAsState e altri.

animateFloatAsState

animateFloatAsState anima un singolo Float. Un uso comune riguarda l'opacità (alpha). Quando visible cambia, l'alpha restituito scorre gradualmente tra 0f e 1f.

Noti la parola chiave by: consente di leggere direttamente lo State<Float> animato come un semplice 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

Le dimensioni e gli offset dell'interfaccia utilizzano Dp, quindi utilizzi animateDpAsState per animare padding, dimensioni o posizione.

Qui un riquadro passa da 80dp a 160dp quando viene attivato. Poiché forniamo il Dp animato a size(), il riquadro cambia dimensione gradualmente.

@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

Per animare i colori, utilizzi animateColorAsState. L'API interpola nello spazio dei colori, così le transizioni risultano fluide e naturali invece di passare bruscamente attraverso tonalità intermedie insolite.

Qui un punto di stato passa gradualmente dal rosso al verde in base a un valore booleano.

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

Personalizzare con animationSpec

Per impostazione predefinita, questi helper utilizzano uno spring delicato. Passi un animationSpec per controllare la temporizzazione. Utilizzi tween per una durata fissa con una curva di easing.

Questo pannello scorre nell'arco di 400ms con una curva fluida che accelera e poi decelera.

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

Curve di easing

L'easing determina il modo in cui un'animazione accelera e decelera. Compose ne include diverse:

  • LinearEasing - velocità costante (sensazione meccanica).
  • FastOutSlowInEasing - il valore predefinito di Material; avvio rapido e conclusione graduale.
  • LinearOutSlowInEasing - per gli elementi che entrano nella schermata.
  • FastOutLinearInEasing - per gli elementi che escono.

Scegliere l'easing giusto rende il movimento intenzionale anziché robotico.

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

Specifiche degli spring

Anziché una durata fissa, può utilizzare uno spring, che dà una sensazione fisica e naturale. Uno spring è definito da dampingRatio (quanto rimbalza) e stiffness (quanto è veloce).

Gli spring sono ottimi perché si adattano gradualmente anche quando l'obiettivo cambia durante l'animazione.

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

Reagire al completamento

A volte è necessario eseguire un'operazione quando un'animazione termina, ad esempio richiamare una callback una volta che un valore raggiunge l'obiettivo. animate*AsState accetta un finishedListener proprio per questo.

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

Animare più proprietà

Può combinare diverse chiamate a animate*AsState per animare più di una proprietà contemporaneamente. Ognuna segue il proprio valore e, insieme, creano un effetto più ricco.

Qui un chip cambia sia scala sia colore quando viene selezionato.

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

Uno spring in puro Kotlin

Per sviluppare un'intuizione su ciò che "fa" uno spring, ecco una piccola simulazione in puro Kotlin. A ogni passaggio spinge un valore verso un obiettivo applicando un certo smorzamento, proprio come lo spring di Compose converge verso il valore obiettivo nell'arco di molti fotogrammi.

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

Animare Int e Offset

La famiglia si estende oltre i float e i colori. Utilizzi animateIntAsState per valori interi, ad esempio il valore obiettivo del contatore di un badge, e animateOffsetAsState o animateIntOffsetAsState per animare la posizione 2D con una sola chiamata.

Qui un chip scorre in diagonale animando un 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)
    )
}

Verifica rapida

Desidera animare gradualmente la larghezza di un riquadro, misurata in Dp, quando cambia un valore booleano. Quale API è più adatta?

Riepilogo: animate*AsState

La famiglia animate*AsState è la scelta principale per animare un singolo valore:

  • Scelga la variante tipizzata: animateFloatAsState, animateDpAsState, animateColorAsState e così via.
  • Imposti un targetValue; il risultato vi si avvicina gradualmente a ogni cambiamento.
  • Regoli il movimento con animationSpec, utilizzando tween con l'easing oppure spring.
  • Ne combini diverse per animare più proprietà insieme.
  • Utilizzi finishedListener per reagire al completamento di un'animazione.

Prossimo argomento: animare la comparsa e la scomparsa dei contenuti con AnimatedVisibility e le transizioni.

Gratis per iniziare

Impara Kotlin con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
36
Lezioni
152

Domande Frequenti

La lezione «Fondamenti di animate*AsState» è gratuita?

Sì — il testo completo di «Fondamenti di animate*AsState» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Android Academy, passa a CoddyKit PRO. Il corso Android Academy include 4 lezioni in totale.

Cosa imparerò in «Fondamenti di animate*AsState»?

Animi fluidamente valori singoli Eserciti Android Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Android Academy?

Non è richiesta alcuna esperienza precedente. Android Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Fondamenti di animate*AsState»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Android Academy?

Sì. Ogni lezione Android Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Perché le animazioni sono importanti
  2. Fondamenti di animate*AsState
  3. AnimatedVisibility e transizioni
  4. Animazioni con gesti e molle
← Torna a Android Academy