Animazione dei movimenti tramite fisica primaverile

Prova Compose
Jetpack Compose è il toolkit per la UI consigliato per Android. Scopri come utilizzare le animazioni in Compose.

Il movimento basato sulla fisica è determinato dalla forza. La forza elastica è una di queste forze che guidano l'interattività e il movimento. Una forza elastica ha le seguenti proprietà: smorzamento e rigidità. In un'animazione basata su una molla, il valore e la velocità vengono calcolati in base alla forza della molla applicata a ogni frame.

Se vuoi che le animazioni della tua app rallentino in una sola direzione, valuta la possibilità di utilizzare un'animazione di scorrimento veloce basata sull'attrito.

Ciclo di vita di un'animazione con effetto molla

In un'animazione basata su una molla, la classe SpringForce ti consente di personalizzare la rigidità della molla, il suo rapporto di smorzamento e la sua posizione finale. Non appena inizia l'animazione, la forza della molla aggiorna il valore dell'animazione e la velocità su ogni fotogramma. L'animazione continua finché la forza della molla non raggiunge un equilibrio.

Ad esempio, se trascini l'icona di un'app sullo schermo e la rilasci sollevando il dito dall'icona, l'icona torna alla sua posizione originale grazie a una forza invisibile ma familiare.

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

Uscita primaverile
Figura 1. Effetto dell'uscita primaverile

Creare un'animazione con effetto molla

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

Le sezioni seguenti descrivono in dettaglio i passaggi generali per creare un'animazione a molla.

Aggiungere la libreria di supporto

Per utilizzare la libreria di supporto basata sulla fisica, devi aggiungerla 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.

    Alla moda

            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 attuali di questa libreria, consulta le informazioni su Dynamicanimation nella pagina Versioni.

Creare un'animazione con effetto molla

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

Nota: al momento della creazione di un'animazione con effetto molla, 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 basata sulla molla può animare le visualizzazioni sullo schermo modificando le proprietà effettive negli oggetti di visualizzazione. Nel sistema sono disponibili le seguenti visualizzazioni:

  • ALPHA: Rappresenta la trasparenza alfa nella visualizzazione. Il valore predefinito è 1 (opaco), mentre un valore pari a 0 rappresenta la trasparenza completa (non visibile).
  • TRANSLATION_X, TRANSLATION_Y e TRANSLATION_Z: queste proprietà controllano la posizione della visualizzazione come delta rispetto alla coordinata sinistra, alla coordinata superiore e all'elevazione, che vengono 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 di rotazione.
  • SCROLL_X e SCROLL_Y: queste proprietà indicano l'offset di scorrimento del bordo sinistro e superiore della sorgente in pixel. Indica anche la posizione in termini di scorrimento della pagina.
  • SCALE_X e SCALE_Y: queste proprietà controllano la scalabilità 2D di una visualizzazione intorno al suo punto di pivot.
  • X, Y e Z: si tratta di proprietà di utilità di base per descrivere la posizione finale della visualizzazione nel relativo contenitore.

Registra i listener

La classe DynamicAnimation fornisce due listener: OnAnimationUpdateListener e OnAnimationEndListener. Questi listener ascoltano gli aggiornamenti dell'animazione, ad esempio quando il valore dell'animazione cambia e quando l'animazione termina.

OnAnimationUpdateListener

Quando vuoi animare più viste per creare un'animazione concatenata, puoi configurare OnAnimationUpdateListener per ricevere un callback ogni volta che si verifica una modifica nella proprietà della vista corrente. Il callback notifica all'altra visualizzazione di aggiornare la posizione della molla in base alla modifica apportata alla proprietà della visualizzazione 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 aggiornamento prima dell'inizio dell'animazione. Tuttavia, il listener di aggiornamento deve essere registrato solo se hai bisogno di un aggiornamento per frame sulle modifiche al valore dell'animazione. Un listener di aggiornamento impedisce che l'animazione venga eseguita su un thread separato.

  2. Esegui l'override del metodo onAnimationUpdate() per notificare al chiamante la modifica dell'oggetto corrente. 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 notifica la fine di un'animazione. Puoi configurare il listener in modo che riceva un 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.

Rimuovere gli ascoltatori

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

Imposta valore iniziale dell'animazione

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

Impostare l'intervallo di valori dell'animazione

Puoi impostare i valori minimo e massimo dell'animazione quando vuoi limitare il valore della proprietà a un determinato intervallo. Inoltre, aiuta a controllare l'intervallo nel caso in cui animi proprietà che hanno un intervallo intrinseco, ad esempio alpha (da 0 a 1).

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

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

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

Imposta velocità iniziale

La velocità iniziale definisce la velocità con cui la proprietà di animazione cambia all'inizio dell'animazione. La velocità iniziale predefinita è impostata su zero pixel al secondo. Puoi impostare la velocità con la velocità dei gesti di tocco 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 poi convertirlo in pixel al secondo. Definire il valore in dp al secondo consente alla velocità di essere indipendente dalla densità e dai fattori di forma. Per ulteriori informazioni sulla conversione del valore in pixel al secondo, consulta la sezione Conversione di dp al secondo in pixel al secondo.

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

Nota:utilizza i metodi delle classi 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 di dp al secondo in pixel al secondo

La velocità di una molla deve essere espressa in pixel al secondo. Se scegli di fornire un valore fisso come inizio della velocità, fornisci il valore in dp al secondo e poi 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());

Imposta le proprietà della molla

La classe SpringForce definisce i metodi getter e 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 su cui impostare le proprietà. Per saperne di più sulla creazione di una forza elastica personalizzata, consulta la sezione Creazione di una forza elastica personalizzata.

Suggerimento:quando utilizzi i metodi setter, puoi creare una catena di metodi, poiché tutti i metodi setter restituiscono l'oggetto della forza elastica.

Coefficiente di smorzamento

Il rapporto di smorzamento descrive una riduzione graduale dell'oscillazione di una molla. Utilizzando il rapporto di smorzamento, puoi definire la velocità con cui le oscillazioni si attenuano da un rimbalzo all'altro. Esistono quattro modi diversi per smorzare una molla:

  • Il sovrasmorzamento si verifica quando il rapporto di smorzamento è maggiore di 1. Consente all'oggetto di tornare delicatamente alla 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 nel minor tempo possibile.
  • Il sottosmorzamento si verifica quando il rapporto di smorzamento è inferiore a 1. Consente all'oggetto di superare più volte la posizione di riposo e poi di raggiungerla gradualmente.
  • L'assenza di smorzamento si verifica quando il rapporto di smorzamento è uguale a zero. Consente all'oggetto di oscillare all'infinito.

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

  1. Chiama il metodo getSpring() per recuperare la molla a cui aggiungere il coefficiente di smorzamento.
  2. Chiama il metodo setDampingRatio() e passa il coefficiente di smorzamento che vuoi aggiungere alla molla. Il metodo restituisce l'oggetto forza elastica su cui è impostato il coefficiente 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 per sempre.

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 rigidità definisce la costante elastica, che misura la forza della molla. Una molla rigida applica più forza all'oggetto a cui è collegata quando non è in posizione di riposo. Per aggiungere la rigidità alla molla, segui questi passaggi:

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

    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: rigidità bassa

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 come alternativa all'utilizzo di quella predefinita. La forza elastica personalizzata ti consente di condividere la stessa istanza di forza elastica in più animazioni elastiche. Una volta creata la forza elastica, puoi impostare proprietà come il rapporto di smorzamento e la rigidità.

  1. Crea un oggetto SpringForce.

    SpringForce force = new SpringForce();

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

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

  3. Chiama il metodo setSpring() per impostare la molla sull'animazione.

    setSpring(force);

Avvia animazione

Esistono due modi per avviare un'animazione con effetto molla: chiamando il metodo 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 è ancora iniziata.

Poiché il metodo aggiorna la posizione finale della molla e avvia l'animazione, se necessario, puoi chiamare questo metodo in qualsiasi momento per modificare il corso di un'animazione. Ad esempio, in un'animazione con effetto molla concatenata, l'animazione di una visualizzazione dipende da un'altra. Per un'animazione di questo tipo, è più comodo utilizzare il metodo animateToFinalPosition(). Se utilizzi questo metodo in un'animazione con effetto molla concatenata, non devi preoccuparti se l'animazione che vuoi aggiornare successivamente è attualmente in esecuzione.

La figura 10 mostra un'animazione con effetto molla concatenata, in cui l'animazione di una visualizzazione dipende da un'altra visualizzazione.

Demo della molla concatenata
Figura 10. Demo della 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 dell'animazione, che si verifica prima del passaggio di disegno. Di conseguenza, le modifiche vengono riflesse 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 o passare alla fine dell'animazione. Una situazione ideale in cui devi annullare o saltare alla fine dell'animazione si verifica quando l'interazione dell'utente richiede l'interruzione immediata dell'animazione. Ciò accade principalmente quando un utente esce bruscamente da un'app o la visualizzazione diventa invisibile.

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

Prima di poter interrompere 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.

Una volta che conosci lo stato della molla, puoi terminare un'animazione utilizzando il metodo skipToEnd() o il metodo cancel(). Il metodo cancel() deve essere chiamato solo sul thread principale.

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