0Pricing
Android Academy · Lezione

Animazioni con gesti e molle

Movimenti naturali basati sulla fisica

Animazioni con gesti e molle è una lezione Android Academy gratuita su CoddyKit. Questa è la lezione 4 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.

Movimento interattivo e fisico

Le animazioni più coinvolgenti rispondono al dito dell'utente e danno la sensazione di oggetti reali, con inerzia e rimbalzo. Per crearle, si combinano gesti e animazioni basate sulla fisica.

Questa lezione tratta Animatable, gli spring, il decadimento del fling e il collegamento con i gesti di trascinamento per realizzare un'interfaccia naturale e interattiva.

Animatable: controllo di basso livello

Mentre animate*AsState reagisce allo stato, Animatable offre un controllo imperativo. Ne conserva un'istanza in remember e la gestisce da una coroutine con chiamate come animateTo o snapTo.

Questa è la base del movimento guidato dai gesti.

@Composable
fun BounceOnClick() {
    val scale = remember { Animatable(1f) }
    val scope = rememberCoroutineScope()
    Box(
        Modifier
            .graphicsLayer { scaleX = scale.value; scaleY = scale.value }
            .clickable {
                scope.launch {
                    scale.animateTo(1.3f, spring())
                    scale.animateTo(1f, spring())
                }
            }
    )
}

Spring: dampingRatio e stiffness

Una specifica spring è definita da due valori:

  • dampingRatio - indica quanto rimbalza. Un valore più basso produce più rimbalzi; 1f (smorzamento critico) non supera l'obiettivo.
  • stiffness - indica la velocità. Un valore più alto raggiunge l'obiettivo più rapidamente.

Compose fornisce costanti come Spring.DampingRatioMediumBouncy e Spring.StiffnessLow, quindi raramente è necessario utilizzare valori numerici grezzi.

val spec = spring<Float>(
    dampingRatio = Spring.DampingRatioLowBouncy,
    stiffness = Spring.StiffnessMedium
)

// later, inside a coroutine:
// offset.animateTo(targetValue = 0f, animationSpec = spec)

Rilevare i gesti di trascinamento

Per rendere interattivo il movimento, si leggono gli input tattili. Modifier.pointerInput con detectDragGestures segnala gli spostamenti durante il trascinamento mentre l'utente muove il dito.

Si accumulano questi spostamenti in un offset e lo si applica al composable.

@Composable
fun Draggable() {
    var offsetX by remember { mutableStateOf(0f) }
    Box(
        Modifier
            .offset { IntOffset(offsetX.roundToInt(), 0) }
            .size(80.dp)
            .background(Color.Red)
            .pointerInput(Unit) {
                detectDragGestures { change, drag ->
                    change.consume()
                    offsetX += drag.x
                }
            }
    )
}

Tornare indietro con una molla

Combini il trascinamento con Animatable per ottenere un effetto di "ritorno a scatto". Durante il trascinamento, usi snapTo per portare istantaneamente l'elemento nella posizione del dito; al rilascio, usi animateTo(0f) con una molla, così l'elemento rimbalza tornando alla posizione iniziale.

@Composable
fun SnapBack() {
    val offsetX = remember { Animatable(0f) }
    val scope = rememberCoroutineScope()
    Box(
        Modifier
            .offset { IntOffset(offsetX.value.roundToInt(), 0) }
            .size(80.dp)
            .background(Color.Blue)
            .pointerInput(Unit) {
                detectDragGestures(
                    onDrag = { change, drag ->
                        change.consume()
                        scope.launch { offsetX.snapTo(offsetX.value + drag.x) }
                    },
                    onDragEnd = {
                        scope.launch { offsetX.animateTo(0f, spring()) }
                    }
                )
            }
    )
}

Lanciare con la velocità

Un lancio realistico continua a muoversi dopo che il dito si solleva. Tieni traccia della velocità con un VelocityTracker, quindi passala a animateDecay, così l'elemento rallenta gradualmente fino a fermarsi in modo naturale usando un DecayAnimationSpec basato sull'attrito.

val decay = rememberSplineBasedDecay<Float>()

// on drag end, with measured velocity:
// scope.launch {
//     offsetX.animateDecay(
//         initialVelocity = velocity,
//         animationSpec = decay
//     )
// }

Swipe per chiudere con gli ancoraggi

Per i gesti di swipe con posizioni di riposo fisse, come lo swipe per chiudere, anchoredDraggable porta l'elemento agli ancoraggi definiti. Dichiari gli stati e le relative posizioni in pixel, mentre Compose gestisce l'animazione a molla tra di essi.

enum class DragValue { Start, End }

val state = remember {
    AnchoredDraggableState(
        initialValue = DragValue.Start,
        anchors = DraggableAnchors {
            DragValue.Start at 0f
            DragValue.End at 300f
        }
    )
}

Animazioni infinite e ripetute

Per movimenti continui, come un punto pulsante o un effetto shimmer di caricamento, usa rememberInfiniteTransition con infiniteRepeatable. Imposta RepeatMode su Reverse per un movimento avanti e indietro, oppure su Restart per ripetere dall'inizio.

@Composable
fun Pulse() {
    val transition = rememberInfiniteTransition(label = "pulse")
    val scale by transition.animateFloat(
        initialValue = 1f,
        targetValue = 1.2f,
        animationSpec = infiniteRepeatable(
            animation = tween(700),
            repeatMode = RepeatMode.Reverse
        ),
        label = "scale"
    )
    Box(Modifier.graphicsLayer { scaleX = scale; scaleY = scale })
}

Combinare gesto e molla

Il modello interattivo per eccellenza: trascinare per muovere, rilasciare per assestare. Tieni traccia della velocità durante il trascinamento, quindi esegui un lancio con animateDecay oppure assesta l'elemento verso un ancoraggio con una spring, in base alla distanza o alla velocità.

È esattamente così che bottom sheet, caroselli e schede chiudibili risultano così naturali.

// pattern:
// 1) onDrag    -> animatable.snapTo(current + delta)
// 2) onDragEnd -> if (fast) animatable.animateDecay(velocity, decay)
//                 else      animatable.animateTo(nearestAnchor, spring())

Assestamento con una molla in puro Kotlin

Per capire come si assesta una molla, ecco un semplice ciclo scritto in puro Kotlin. Applica una forza di richiamo verso l'obiettivo e smorza la velocità a ogni passaggio: è la stessa idea che Compose esegue a ogni frame.

fun main() {
    var pos = 0f
    var vel = 0f
    val target = 200f
    val stiffness = 0.2f
    val damping = 0.75f
    repeat(8) { step ->
        vel = (vel + (target - pos) * stiffness) * damping
        pos += vel
        println("step $step pos=${"%.1f".format(pos)}")
    }
}

Tocco, pressione e gesti multipli

Il trascinamento non è l'unico gesto. pointerInput offre anche detectTapGestures per tocchi, doppi tocchi e pressioni prolungate, oltre a detectTransformGestures per lo zoom tramite pizzicamento e la rotazione.

Qui una pressione prolungata attiva una variazione di scala con una molla, combinando il rilevamento dei gesti con un Animatable.

@Composable
fun LongPressScale() {
    val scale = remember { Animatable(1f) }
    val scope = rememberCoroutineScope()
    Box(
        Modifier
            .graphicsLayer { scaleX = scale.value; scaleY = scale.value }
            .pointerInput(Unit) {
                detectTapGestures(
                    onLongPress = {
                        scope.launch { scale.animateTo(1.4f, spring()) }
                    },
                    onPress = {
                        tryAwaitRelease()
                        scope.launch { scale.animateTo(1f, spring()) }
                    }
                )
            }
    )
}

Verifica rapida

Stai creando una scheda trascinabile che, al rilascio, dovrebbe rimbalzare tornando all'origine. Quale combinazione è la scelta idiomatica in Compose?

Riepilogo: gesti e molle

Ora è possibile creare movimenti naturali e interattivi:

  • Animatable offre un controllo imperativo guidato da coroutine tramite animateTo / snapTo.
  • spring si configura con dampingRatio (rimbalzo) e stiffness (velocità).
  • pointerInput + detectDragGestures trasformano il tocco in movimento; animateDecay aggiunge il lancio.
  • anchoredDraggable porta l'elemento a posizioni fisse per le interazioni di swipe.
  • rememberInfiniteTransition gestisce effetti ripetuti come pulsazioni e shimmer.

Con questo si conclude la lezione sulle animazioni in Jetpack Compose: ora è possibile animare con sicurezza valori, visibilità, contenuti e gesti.

Domande Frequenti

La lezione «Animazioni con gesti e molle» è gratuita?

Sì — il testo completo di «Animazioni con gesti e molle» è 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 «Animazioni con gesti e molle»?

Movimenti naturali basati sulla fisica 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 4 di 4.

Quanto tempo richiede la lezione «Animazioni con gesti e molle»?

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