Animazione dei movimenti tramite fisica primaverile

Prova la modalità Scrivi
Jetpack Compose è il toolkit dell'interfaccia utente consigliato per Android. Scopri come utilizzare le animazioni in Compose.

Il movimento basato sulla fisica è determinato dalla forza. La forza della molla è una delle forze che guida l'interattività e il movimento. Una forza della molla ha le seguenti proprietà: smorzamento e rigidità. In un'animazione a molla, il valore e la velocità vengono calcolati in base alla forza della molla applicata a ciascun fotogramma.

Se vuoi che le animazioni dell'app rallentino solo in una direzione, valuta la possibilità di utilizzare un'animazione flessione basata sull'attrito.

Ciclo di vita di un'animazione primaverile

In un'animazione a molla, la classe SpringForce consente di personalizzare la rigidità della molla, il rapporto di smorzamento e la posizione finale. Non appena inizia l'animazione, la forza della molla aggiorna il valore dell'animazione e la velocità su ciascun frame. L'animazione continua finché la forza della molla non raggiunge un equilibrio.

Ad esempio, se trascini l'icona di un'app sullo schermo e poi la rilasci sollevando il dito dall'icona, l'icona torna nella posizione originale di una forza invisibile ma nota.

La Figura 1 mostra un effetto elastico simile. Il segno più (+) al centro del cerchio indica la forza applicata tramite un gesto di tocco.

Rilascio di primavera
Figura 1. Effetto rilascio primavera

Creare un'animazione primaverile

I passaggi generali per creare un'animazione molla per la tua applicazione sono i seguenti:

Le seguenti sezioni descrivono in dettaglio i passaggi generali per la creazione di un'animazione primavera.

Aggiungi la libreria di supporto

Per utilizzare la libreria di supporto basata sulla fisica, devi aggiungere la libreria di supporto al tuo progetto come segue:

  1. Apri il file build.gradle per il modulo dell'app.
  2. Aggiungi la libreria di supporto alla sezione dependencies.

    trendy

            dependencies {
                def dynamicanimation_version = '1.0.0'
                implementation "androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version"
            }
            

    Kotlin

            dependencies {
                val dynamicanimation_version = "1.0.0"
                implementation("androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version")
            }
            

    Per visualizzare le versioni correnti di questa libreria, vedi le informazioni su Dynamicanimation nella pagina versions.

Creare un'animazione primaverile

La classe SpringAnimation consente di creare un'animazione primaverile per un oggetto. Per realizzare un'animazione primaverile, devi creare un'istanza della classe SpringAnimation e fornire un oggetto, la proprietà di un oggetto che vuoi animare e una posizione finale facoltativa della molla in cui vuoi che si fermi.

Nota: al momento della creazione di un'animazione primaverile, la posizione finale della molla è facoltativa. Tuttavia, deve essere definito prima di avviare l'animazione.

Kotlin

val springAnim = findViewById<View>(R.id.imageView).let { img ->
    // Setting up a spring animation to animate the view’s translationY property with the final
    // spring position at 0.
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0f)
}

Java

final View img = findViewById(R.id.imageView);
// Setting up a spring animation to animate the view’s translationY property with the final
// spring position at 0.
final SpringAnimation springAnim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0);

L'animazione a molla può animare le visualizzazioni sullo schermo modificando le proprietà effettive degli oggetti vista. Nel sistema sono disponibili le seguenti visualizzazioni:

  • ALPHA: rappresenta la trasparenza alpha della vista. Per impostazione predefinita, il valore è 1 (opaco) e il valore 0 rappresenta la massima trasparenza (non visibile).
  • TRANSLATION_X, TRANSLATION_Y e TRANSLATION_Z: queste proprietà controllano la posizione della vista come delta rispetto alla coordinata sinistra, alla coordinata superiore e all'elevazione, che sono impostate dal relativo contenitore di layout.
  • ROTATION, ROTATION_X e ROTATION_Y: queste proprietà controllano la rotazione in 2D (proprietà rotation) e in 3D intorno al punto pivot.
  • SCROLL_X e SCROLL_Y: queste proprietà indicano l'offset di scorrimento dell'origine sinistra e il bordo superiore in pixel. Indica anche la posizione in termini di quanto è stata fatta scorrere la pagina.
  • SCALE_X e SCALE_Y: queste proprietà controllano la scalabilità 2D di una vista intorno al relativo punto pivot.
  • X, Y e Z: queste sono proprietà di utilità di base che descrivono la posizione finale della vista nel relativo container.

Registra listener

La classe DynamicAnimation offre due listener: OnAnimationUpdateListener e OnAnimationEndListener. Questi listener ascoltano gli aggiornamenti dell'animazione, ad esempio quando si verifica una modifica nel valore dell'animazione e quando l'animazione termina.

OnAnimationUpdateListener

Se vuoi animare più viste per creare un'animazione concatenata, puoi impostare OnAnimationUpdateListener in modo che riceva un callback ogni volta che viene apportata una modifica alla proprietà della vista corrente. Il callback avvisa l'altra vista di aggiornare la sua posizione primaverile in base alla modifica effettuata nella proprietà della vista corrente. Per registrare il listener, segui questi passaggi:

  1. Chiama il metodo addUpdateListener() e collega il listener all'animazione.

    Nota: devi registrare il listener di aggiornamenti prima dell'inizio dell'animazione. Tuttavia, il listener di aggiornamenti deve essere registrato solo se devi eseguire un aggiornamento per frame alle modifiche dei valori dell'animazione. Un listener di aggiornamenti impedisce la potenziale esecuzione dell'animazione in un thread separato.

  2. Esegui l'override del metodo onAnimationUpdate() per notificare al chiamante la modifica dell'oggetto attuale. Il seguente codice campione illustra l'utilizzo complessivo di OnAnimationUpdateListener.

Kotlin

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
val (anim1X, anim1Y) = findViewById<View>(R.id.view1).let { view1 ->
    SpringAnimation(view1, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y)
}
val (anim2X, anim2Y) = findViewById<View>(R.id.view2).let { view2 ->
    SpringAnimation(view2, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y)
}

// Registering the update listener
anim1X.addUpdateListener { _, value, _ ->
    // Overriding the method to notify view2 about the change in the view1’s property.
    anim2X.animateToFinalPosition(value)
}

anim1Y.addUpdateListener { _, value, _ -> anim2Y.animateToFinalPosition(value) }

Java

// Creating two views to demonstrate the registration of the update listener.
final View view1 = findViewById(R.id.view1);
final View view2 = findViewById(R.id.view2);

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
final SpringAnimation anim1X = new SpringAnimation(view1,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim1Y = new SpringAnimation(view1,
    DynamicAnimation.TRANSLATION_Y);
final SpringAnimation anim2X = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim2Y = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_Y);

// Registering the update listener
anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

// Overriding the method to notify view2 about the change in the view1’s property.
    @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2X.animateToFinalPosition(value);
    }
});

anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

  @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2Y.animateToFinalPosition(value);
    }
});

OnAnimationEndListener

OnAnimationEndListener avvisa la fine di un'animazione. Puoi configurare il listener in modo che riceva il callback ogni volta che l'animazione raggiunge l'equilibrio o viene annullata. Per registrare il listener, segui questi passaggi:

  1. Chiama il metodo addEndListener() e collega il listener all'animazione.
  2. Esegui l'override del metodo onAnimationEnd() per ricevere una notifica ogni volta che un'animazione raggiunge l'equilibrio o viene annullata.

Rimuovi listener

Per interrompere la ricezione dei callback di aggiornamento dell'animazione e del callback di fine animazione, chiama rispettivamente i metodi removeUpdateListener() e removeEndListener().

Imposta valore iniziale animazione

Per impostare il valore iniziale dell'animazione, chiama il metodo setStartValue() e trasmetti il valore iniziale dell'animazione. Se non imposti il valore iniziale, l'animazione utilizza il valore corrente della proprietà dell'oggetto come valore iniziale.

Imposta intervallo di valori dell'animazione

Puoi impostare i valori minimi e massimi dell'animazione quando vuoi limitare il valore della proprietà a un determinato intervallo. Inoltre, è utile controllare l'intervallo nel caso in cui tu abbia animato proprietà che hanno un intervallo intrinseco, come alfa (da 0 a 1).

  • Per impostare il valore minimo, chiama il metodo setMinValue() e trasmetti il valore minimo della proprietà.
  • Per impostare il valore massimo, chiama il metodo setMaxValue() e trasmetti il valore massimo della proprietà.

Entrambi i metodi restituiscono l'animazione per la quale viene impostato il valore.

Nota: se hai impostato il valore iniziale e hai definito un intervallo di valori dell'animazione, assicurati che il valore iniziale rientri nell'intervallo di valori minimo e massimo.

Imposta velocità iniziale

La velocità di avvio definisce la velocità con cui cambia la proprietà dell'animazione all'inizio dell'animazione. La velocità iniziale predefinita è impostata su zero pixel al secondo. Puoi impostare la velocità con la velocità dei gesti tattili o utilizzando un valore fisso come velocità iniziale. Se scegli di fornire un valore fisso, ti consigliamo di definire il valore in dp al secondo e di convertirlo in pixel al secondo. La definizione del valore in dp al secondo consente alla velocità di essere indipendente dalla densità e dai fattori di forma. Per saperne di più sulla conversione del valore in pixel al secondo, consulta la sezione Conversione dei dp al secondo in pixel al secondo.

Per impostare la velocità, chiama il metodo setStartVelocity() e trasmettila in pixel al secondo. Il metodo restituisce l'oggetto di forza della molla su cui è impostata la velocità.

Nota: utilizza i metodi di classe GestureDetector.OnGestureListener o VelocityTracker per recuperare e calcolare la velocità dei gesti tattili.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Compute velocity in the unit pixel/second
        vt.computeCurrentVelocity(1000)
        val velocity = vt.yVelocity
        setStartVelocity(velocity)
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Compute velocity in the unit pixel/second
vt.computeCurrentVelocity(1000);
float velocity = vt.getYVelocity();
anim.setStartVelocity(velocity);

Conversione dei dp al secondo in pixel al secondo

La velocità di una molla deve essere in pixel al secondo. Se scegli di fornire un valore fisso come inizio della velocità, indica il valore in dp al secondo, quindi convertilo in pixel al secondo. Per la conversione, utilizza il metodo applyDimension() della classe TypedValue. Fai riferimento al seguente codice campione:

Kotlin

val pixelPerSecond: Float =
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, resources.displayMetrics)

Java

float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, getResources().getDisplayMetrics());

Impostare le proprietà della molla

La classe SpringForce definisce il getter e i metodi del setter per ciascuna delle proprietà della molla, come il rapporto di smorzamento e la rigidità. Per impostare le proprietà della molla, è importante recuperare l'oggetto forza della molla o creare una forza della molla personalizzata sulla quale poter impostare le proprietà. Per ulteriori informazioni sulla creazione di una forza della molla personalizzata, consulta la sezione Creare una forza della molla personalizzata.

Suggerimento: quando utilizzi i metodi del setter, puoi creare una catena di metodi poiché tutti i metodi del setter restituiscono l'oggetto spring force.

Rapporto di attenuazione

Il rapporto di smorzamento descrive una graduale riduzione dell'oscillazione della molla. L'utilizzo del rapporto di smorzamento ti consente di definire la velocità di decadimento delle oscillazioni da un rimbalzo all'altro. Esistono quattro modi diversi per umidificare una molla:

  • Il sovrasmorzamento si verifica quando il rapporto di smorzamento è maggiore di uno. Consente all'oggetto di tornare delicatamente nella posizione di riposo.
  • Lo smorzamento critico si verifica quando il rapporto di smorzamento è uguale a uno. Consente all'oggetto di tornare alla posizione di riposo nell'arco di tempo più breve.
  • Il sottosmorzamento si verifica quando il rapporto di smorzamento è inferiore a uno. Consente di superare l'oggetto in eccesso superando la posizione di riposo, per poi raggiungere gradualmente la posizione di riposo.
  • Lo smorzamento si verifica quando il rapporto di smorzamento è pari a zero. Consente all'oggetto di oscillare all'infinito.

Per aggiungere il rapporto di smorzamento alla molla, segui questi passaggi:

  1. Richiama il metodo getSpring() per recuperare la molla e aggiungere il rapporto di smorzamento.
  2. Richiama il metodo setDampingRatio() e trasmetti il rapporto di smorzamento che vuoi aggiungere alla molla. Il metodo restituisce l'oggetto di forza della molla su cui è impostato il rapporto di smorzamento.

    Nota: il rapporto di smorzamento deve essere un numero non negativo. Se imposti il rapporto di smorzamento su zero, la molla non raggiungerà mai la posizione di riposo. In altre parole, oscilla all'infinito.

Nel sistema sono disponibili le seguenti costanti del rapporto di smorzamento:

Figura 2: rimbalzo elevato

Figura 3: rimbalzo medio

Figura 4: rimbalzo basso

Figura 5: nessun rimbalzo

Il rapporto di smorzamento predefinito è impostato su DAMPING_RATIO_MEDIUM_BOUNCY.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Setting the damping ratio to create a low bouncing effect.
        spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Setting the damping ratio to create a low bouncing effect.
anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
…

Rigidità

La rigidezza definisce la costante della molla, che misura la forza della molla. Una molla rigida applica più forza all'oggetto attaccato quando la molla non è in posizione di riposo. Per aumentare la rigidità della molla, segui questi passaggi:

  1. Richiama il metodo getSpring() per recuperare la molla e aggiungere la rigidità.
  2. Richiama il metodo setStiffness() e trasmetti il valore di rigidità che vuoi aggiungere alla molla. Il metodo restituisce l'oggetto della forza della molla su cui è impostata la rigidezza.

    Nota: la rigidità deve essere un numero positivo.

Nel sistema sono disponibili le seguenti costanti di rigidità:

Figura 6: rigidità elevata

Figura 7: rigidità media

Figura 8: bassa rigidità

Figura 9: rigidità molto bassa

La rigidità predefinita è impostata su STIFFNESS_MEDIUM.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Setting the spring with a low stiffness.
        spring.stiffness = SpringForce.STIFFNESS_LOW
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Setting the spring with a low stiffness.
anim.getSpring().setStiffness(SpringForce.STIFFNESS_LOW);
…

Creare una forza della molla personalizzata

Puoi creare una forza della molla personalizzata in alternativa all'utilizzo della forza della molla predefinita. La forza della molla personalizzata consente di condividere la stessa istanza della forza della molla tra più animazioni della molla. Dopo aver creato la forza della molla, puoi impostare proprietà come il rapporto di smorzamento e la rigidità.

  1. Crea un oggetto SpringForce.

    SpringForce force = new SpringForce();

  2. Assegna le proprietà richiamando i rispettivi metodi. Puoi anche creare una catena di metodi.

    force.setDampingRatio(DAMPING_RATIO_LOW_BOUNCY).setStiffness(STIFFNESS_LOW);

  3. Richiama il metodo setSpring() per impostare la molla per l'animazione.

    setSpring(force);

Avvia animazione

Esistono due modi per avviare un'animazione primaverile: richiamando start() o il metodo animateToFinalPosition(). Entrambi i metodi devono essere chiamati sul thread principale.

animateToFinalPosition() esegue due attività:

  • Imposta la posizione finale della molla.
  • Avvia l'animazione, se non è stata avviata.

Poiché il metodo aggiorna la posizione finale della molla e, se necessario, avvia l'animazione, puoi richiamare questo metodo in qualsiasi momento per modificare il corso di un'animazione. Ad esempio, in un'animazione molla concatenata, l'animazione di una vista dipende da un'altra. Per un'animazione di questo tipo, è più pratico utilizzare il metodo animateToFinalPosition(). Se utilizzi questo metodo in un'animazione a molla concatenata, non dovrai preoccuparti se l'animazione successiva da aggiornare è attualmente in esecuzione.

La Figura 10 illustra un'animazione a molla concatenata, in cui l'animazione di una vista dipende da un'altra.

Demo di Chained Spring
Figura 10. Demo relativa alla molla concatenata

Per utilizzare il metodo animateToFinalPosition(), chiama il metodo animateToFinalPosition() e passa la posizione di riposo della molla. Puoi anche impostare la posizione di riposo della molla chiamando il metodo setFinalPosition().

Il metodo start() non imposta immediatamente il valore della proprietà sul valore iniziale. Il valore della proprietà cambia a ogni impulso di animazione, che si verifica prima del passaggio di disegno. Di conseguenza, le modifiche si riflettono nel frame successivo, come se i valori fossero impostati immediatamente.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Starting the animation
        start()
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Starting the animation
anim.start();
…

Annulla animazione

Puoi annullare l'animazione o saltarla alla fine. La situazione ideale in cui devi annullare o passare alla fine dell'animazione è quando un'interazione utente richiede l'interruzione immediata dell'animazione. Questo accade principalmente quando un utente esce improvvisamente da un'app o la visualizzazione diventa invisibile.

Puoi utilizzare due metodi per terminare l'animazione. Il metodo cancel() termina l'animazione in base al valore in cui si trova. Il metodo skipToEnd() salta l'animazione al valore finale e poi la termina.

Prima di poter terminare l'animazione, è importante controllare lo stato della molla. Se lo stato non è smorzato, l'animazione non può mai raggiungere la posizione di riposo. Per controllare lo stato della molla, chiama il metodo canSkipToEnd(). Se la molla è smorzata, il metodo restituisce true, altrimenti false.

Quando conosci lo stato della molla, puoi terminare un'animazione utilizzando i metodi skipToEnd() o cancel(). Il metodo cancel() deve essere chiamato solo sul thread principale.

Nota: in generale, il metodo skipToEnd() determina un salto visivo.