Panoramica dell'animazione della proprietà

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

Il sistema di animazione delle proprietà è un solido framework che consente di animare quasi tutto. Puoi definire un'animazione per modificare qualsiasi proprietà dell'oggetto nel tempo, indipendentemente dal fatto che venga attirato sullo schermo o meno. L'animazione di una proprietà modifica il valore di una proprietà (un campo in un oggetto) entro un periodo di tempo specificato. Per animare un elemento, specifica la proprietà dell'oggetto che vuoi animare, ad esempio la posizione di un oggetto sullo schermo, la durata dell'animazione e i valori tra i quali vuoi animare.

Il sistema di animazione delle proprietà consente di definire le seguenti caratteristiche di un'animazione:

  • Durata: puoi specificare la durata di un'animazione. La lunghezza predefinita è 300 ms.
  • Interpolazione temporale: puoi specificare il modo in cui vengono calcolati i valori della proprietà come funzione del tempo trascorso attuale dell'animazione.
  • Conteggio e comportamento ripetizioni: puoi specificare se un'animazione si ripete quando raggiunge la fine della durata e quante volte deve ripeterla. Puoi anche specificare se vuoi che l'animazione venga riprodotta al contrario. Impostando la modalità inversa, l'animazione viene riprodotta in avanti e indietro ripetutamente fino a raggiungere il numero di ripetizioni.
  • Set di animazioni: puoi raggruppare le animazioni in set logici che vengono riprodotti insieme o in sequenza o dopo determinati ritardi.
  • Ritardo aggiornamento frame: puoi specificare la frequenza di aggiornamento dei frame dell'animazione. L'impostazione predefinita è impostata per l'aggiornamento ogni 10 ms, ma la velocità con cui l'applicazione può aggiornare i frame dipende in ultima analisi dall'affollamento complessivo del sistema e dalla velocità con cui il sistema può gestire il timer sottostante.

Per un esempio completo di animazione della proprietà, vedi la classe ChangeColor nell'esempio di CustomTransaction su GitHub.

Come funziona l'animazione della proprietà

Innanzitutto, vediamo come funziona un'animazione con un semplice esempio. La Figura 1 mostra un oggetto ipotetico animato con la sua proprietà x, che rappresenta la sua posizione orizzontale su uno schermo. La durata dell'animazione è impostata su 40 ms e la distanza da percorrere è di 40 pixel. Ogni 10 ms, che è la frequenza di aggiornamento predefinita del frame, l'oggetto si sposta orizzontalmente di 10 pixel. Dopo 40 ms, l'animazione si interrompe e l'oggetto termina nella posizione orizzontale 40. Questo è un esempio di animazione con interpolazione lineare, vale a dire che l'oggetto si muove a una velocità costante.

Figura 1. Esempio di animazione lineare

Puoi anche specificare le animazioni in modo che abbiano un'interpolazione non lineare. La Figura 2 illustra un oggetto ipotetico che accelera all'inizio dell'animazione e rallenta alla fine dell'animazione. L'oggetto si sposta comunque di 40 pixel in 40 ms, ma in modo non lineare. All'inizio, questa animazione accelera fino al punto metà dell'animazione, poi rallenterà dal punto a metà fino alla fine dell'animazione. Come mostra la Figura 2, la distanza percorsa all'inizio e alla fine dell'animazione è inferiore a quella centrale.

Figura 2. Esempio di animazione non lineare

Analizziamo nel dettaglio in che modo i componenti importanti del sistema di animazione della proprietà calcolano le animazioni come quelle illustrate sopra. La figura 3 mostra il funzionamento reciprocamente delle classi principali.

Figura 3. Come vengono calcolate le animazioni

L'oggetto ValueAnimator tiene traccia dei tempi dell'animazione, ad esempio la durata dell'animazione e il valore corrente della proprietà che anima.

ValueAnimator incapsula un TimeInterpolator, che definisce l'interpolazione dell'animazione, e un TypeEvaluator, che definisce le modalità di calcolo dei valori per la proprietà animata. Ad esempio, nella Figura 2, il valore TimeInterpolator utilizzato è AccelerateDecelerateInterpolator e TypeEvaluator è IntEvaluator.

Per avviare un'animazione, crea un ValueAnimator e assegnagli i valori iniziale e finale per la proprietà da animare, insieme alla durata dell'animazione. Quando chiami start(), viene avviata l'animazione. Durante l'intera animazione, ValueAnimator calcola una frazione trascorsa compresa tra 0 e 1, in base alla durata dell'animazione e al tempo trascorso. La frazione trascorsa rappresenta la percentuale di tempo di completamento dell'animazione. 0 indica 0% e 1 indica il 100%. Ad esempio, nella Figura 1, la frazione trascorsa in t = 10 ms sarebbe 0,25 perché la durata totale è t = 40 ms.

Quando ValueAnimator ha terminato di calcolare una frazione trascorsa, chiama il valore TimeInterpolator attualmente impostato per calcolare una frazione interpolata. Una frazione interpolata mappa la frazione trascorsa a una nuova frazione che tiene conto dell'interpolazione temporale impostata. Ad esempio, nella Figura 2, perché l'animazione accelera lentamente, la frazione interpolata, circa 0,15, è inferiore alla frazione trascorso, ovvero 0,25, con t = 10 ms. Nella Figura 1, la frazione interpolata è sempre uguale alla frazione trascorsa.

Quando viene calcolata la frazione interpolata, ValueAnimator chiama il valore TypeEvaluator appropriato, per calcolare il valore della proprietà che stai animando, in base alla frazione interpolata, al valore iniziale e al valore finale dell'animazione. Ad esempio, nella Figura 2, la frazione interpolata era 0,15 at t = 10 ms, quindi il valore per la proprietà in quel momento sarebbe 0,15 × (40 - 0) o 6.

Differenza tra l'animazione della proprietà e l'animazione della vista

Il sistema di animazione delle viste consente di animare solo oggetti View, quindi per animare oggetti non View è necessario implementare codice personalizzato. Il sistema di animazione della visualizzazione è inoltre vincolato dal fatto che espone solo alcuni aspetti di un oggetto View da animare, come il ridimensionamento e la rotazione di una vista, ma non il colore di sfondo.

Un altro svantaggio del sistema di animazione delle viste è che ha modificato solo il punto in cui era disegnata la vista, non la vista effettiva. Ad esempio, se hai animato un pulsante in modo che si sposti sullo schermo, il pulsante viene tracciato correttamente, ma la posizione effettiva in cui puoi fare clic non cambia, quindi devi implementare una logica per gestire questa situazione.

Con il sistema di animazione delle proprietà, questi vincoli vengono completamente rimossi e puoi animare qualsiasi proprietà di qualsiasi oggetto (View e non View) determinando la modifica dell'oggetto stesso. Il sistema di animazione della proprietà è inoltre più efficace nella realizzazione dell'animazione. A livello generale, assegna animatori alle proprietà che vuoi animare, come colore, posizione o dimensione, e puoi definire aspetti dell'animazione come l'interpolazione e la sincronizzazione di più animatori.

Il sistema di animazione della visualizzazione, tuttavia, richiede meno tempo per la configurazione e meno codice da scrivere. Se l'animazione di una vista ti permette di fare tutto ciò che devi fare o se il codice esistente funziona già come vuoi tu, non è necessario utilizzare il sistema di animazione delle proprietà. Potrebbe anche avere senso utilizzare entrambi i sistemi di animazione per situazioni diverse, qualora si presentasse il caso d'uso.

Panoramica dell'API

Puoi trovare la maggior parte delle API del sistema di animazione delle proprietà in android.animation. Poiché il sistema di animazione delle viste definisce già molti interpolatori in android.view.animation, puoi utilizzare questi interpolatori anche nel sistema di animazione delle proprietà. Le seguenti tabelle descrivono i componenti principali del sistema di animazione delle proprietà.

La classe Animator fornisce la struttura di base per la creazione delle animazioni. Normalmente non puoi utilizzare direttamente questa classe in quanto fornisce solo funzionalità minime che devono essere estese per supportare completamente i valori di animazione. Le seguenti sottoclassi estendono Animator:

Tabella 1. Animatori

Classe Descrizione
ValueAnimator Il motore di temporizzazione principale per l'animazione delle proprietà, che calcola anche i valori per la proprietà da animare. Include tutte le funzionalità di base per calcolare i valori dell'animazione e contiene i dettagli sui tempi di ogni animazione, informazioni sulla ripetizione di un'animazione, listener che ricevono eventi di aggiornamento e la possibilità di impostare tipi personalizzati da valutare. L'animazione delle proprietà comprende due parti: il calcolo dei valori animati e l'impostazione di questi valori sull'oggetto e sulla proprietà animata. ValueAnimator non esegue la seconda parte, quindi devi esaminare gli aggiornamenti dei valori calcolati da ValueAnimator e modificare gli oggetti che vuoi animare con la tua logica. Per ulteriori informazioni, consulta la sezione relativa all'animazione con ValueAnimator.
ObjectAnimator Una sottoclasse di ValueAnimator che consente di impostare un oggetto e una proprietà dell'oggetto target da animare. Questa classe aggiorna la proprietà di conseguenza quando calcola un nuovo valore per l'animazione. Vuoi utilizzare ObjectAnimator la maggior parte delle volte, perché rende molto più semplice il processo di animazione dei valori sugli oggetti di destinazione. Tuttavia, a volte potresti voler utilizzare direttamente ValueAnimator perché ObjectAnimator prevede alcune limitazioni aggiuntive, ad esempio la richiesta di metodi di accesso specifici nell'oggetto di destinazione.
AnimatorSet Fornisce un meccanismo per raggruppare le animazioni in modo che vengano eseguite l'una rispetto all'altra. Puoi impostare le animazioni in modo che vengano riprodotte insieme, in sequenza o dopo un determinato ritardo. Per ulteriori informazioni, consulta la sezione sulla Coreografia di più animazioni con set di animazioni.

I valutatori indicano al sistema di animazione delle proprietà come calcolare i valori per una determinata proprietà. Prendono i dati di temporizzazione forniti da una classe Animator, i valori di inizio e fine dell'animazione, e calcolano i valori animati della proprietà in base a questi dati. Il sistema di animazione delle proprietà fornisce i seguenti valutatori:

Tabella 2. Valutatori

Classe/interfaccia Descrizione
IntEvaluator Il valutatore predefinito per calcolare i valori per le proprietà int.
FloatEvaluator Il valutatore predefinito per calcolare i valori per le proprietà float.
ArgbEvaluator Lo strumento di valutazione predefinito per calcolare i valori per le proprietà del colore rappresentate come valori esadecimali.
TypeEvaluator Un'interfaccia che ti consente di creare un tuo strumento di valutazione. Se stai animando una proprietà di oggetto che non è int, float o colore, devi implementare l'interfaccia TypeEvaluator per specificare come calcolare i valori animati della proprietà dell'oggetto. Puoi anche specificare un valore TypeEvaluator personalizzato per i valori di int, float e colore, se vuoi elaborare questi tipi in modo diverso rispetto al comportamento predefinito. Consulta la sezione sull'utilizzo di un TypeEvaluator per ulteriori informazioni su come scrivere un valutatore personalizzato.

Un interpolatore temporale definisce il modo in cui i valori specifici di un'animazione vengono calcolati come funzione del tempo. Ad esempio, puoi specificare che le animazioni vengano visualizzate in modo lineare su tutta l'animazione, ovvero che si sposti in modo uniforme per tutto il tempo, oppure puoi specificare animazioni per utilizzare un tempo non lineare, ad esempio l'accelerazione all'inizio e la decelerazione alla fine dell'animazione. La tabella 3 descrive gli interpolatori contenuti in android.view.animation. Se nessuno degli interpolatori forniti soddisfa le tue esigenze, implementa l'interfaccia TimeInterpolator e creane uno personalizzato. Per ulteriori informazioni su come scrivere un interpolatore personalizzato, consulta la sezione Utilizzo degli interpolatori.

Tabella 3. Interpolatori

Classe/interfaccia Descrizione
AccelerateDecelerateInterpolator Un interpolatore la cui frequenza di cambiamento inizia e termina lentamente, ma accelera attraverso il mezzo.
AccelerateInterpolator Un interpolatore la cui frequenza di cambiamento inizia lentamente e poi accelera.
AnticipateInterpolator Un interpolatore il cui cambiamento inizia indietro e si sposta in avanti.
AnticipateOvershootInterpolator Un interpolatore la cui modifica inizia all'indietro, si sposta in avanti e supera il valore target, per poi tornare al valore finale.
BounceInterpolator Un interpolatore la cui modifica rimbalza alla fine.
CycleInterpolator Un interpolatore la cui animazione si ripete per un determinato numero di cicli.
DecelerateInterpolator Un interpolatore la cui frequenza di modifica inizia rapidamente e poi rallenta.
LinearInterpolator Un interpolatore il cui tasso di variazione è costante.
OvershootInterpolator Un interpolatore la cui modifica si lancia in avanti e supera l'ultimo valore, poi torna indietro.
TimeInterpolator Un'interfaccia che consente di implementare un interpolatore personalizzato.

Creazione di animazioni con ValueAnimator

La classe ValueAnimator ti consente di animare valori di qualche tipo per la durata di un'animazione specificando un insieme di valori int, float o colore attraverso i quali animare. Puoi ottenere un ValueAnimator chiamando uno dei suoi metodi di fabbrica: ofInt(), ofFloat() o ofObject(). Ecco alcuni esempi:

Kotlin

ValueAnimator.ofFloat(0f, 100f).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();

In questo codice, ValueAnimator inizia a calcolare i valori dell'animazione, compresi tra 0 e 100, per una durata di 1000 ms, quando viene eseguito il metodo start().

Puoi anche specificare un tipo personalizzato da animare procedendo nel seguente modo:

Kotlin

ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

In questo codice, ValueAnimator inizia a calcolare i valori dell'animazione, tra startPropertyValue e endPropertyValue, utilizzando la logica fornita da MyTypeEvaluator per una durata di 1000 ms, quando viene eseguito il metodo start().

Puoi utilizzare i valori dell'animazione aggiungendo AnimatorUpdateListener all'oggetto ValueAnimator, come mostrato nel seguente codice:

Kotlin

ValueAnimator.ofObject(...).apply {
    ...
    addUpdateListener { updatedAnimation ->
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        textView.translationX = updatedAnimation.animatedValue as Float
    }
    ...
}

Java

animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

Nel metodo onAnimationUpdate() puoi accedere al valore aggiornato dell'animazione e utilizzarlo in una proprietà di una delle tue viste. Per ulteriori informazioni sui listener, consulta la sezione sui Listener di animazione.

Creazione di animazioni con ObjectAnimator

ObjectAnimator è una sottoclasse di ValueAnimator (esaminata nella sezione precedente) e combina il calcolo del motore di tempo e del valore di ValueAnimator con la possibilità di animare una proprietà con nome di un oggetto target. In questo modo, l'animazione di qualsiasi oggetto è molto più semplice, in quanto non è più necessario implementare ValueAnimator.AnimatorUpdateListener, poiché la proprietà animata si aggiorna automaticamente.

La creazione di un'istanza per un ObjectAnimator è simile a un ValueAnimator, ma puoi specificare anche l'oggetto e il nome della sua proprietà (come stringa) insieme ai valori da animare tra:

Kotlin

ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
    duration = 1000
    start()
}

Java

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

Per fare in modo che le proprietà di aggiornamento di ObjectAnimator siano corrette, devi seguire questi passaggi:

  • La proprietà dell'oggetto che stai animando deve avere una funzione setter (in caso di cammello) nel formato set<PropertyName>(). Poiché ObjectAnimator aggiorna automaticamente la proprietà durante l'animazione, deve essere in grado di accedere alla proprietà con questo metodo di impostazione. Ad esempio, se il nome della proprietà è foo, devi avere un metodo setFoo(). Se questo metodo di impostazione non esiste, hai tre opzioni:
    • Se disponi dei diritti necessari, aggiungi alla classe il metodo di impostazione.
    • Utilizza una classe wrapper che hai il diritto di modificare e che il wrapper riceva il valore con un metodo di setter valido e lo inoltri all'oggetto originale.
    • Usa invece il criterio ValueAnimator.
  • Se specifichi un solo valore per il parametro values... in uno dei metodi di fabbrica ObjectAnimator, si presume che sia il valore finale dell'animazione. Pertanto, la proprietà dell'oggetto che stai animando deve avere una funzione getter utilizzata per ottenere il valore iniziale dell'animazione. La funzione getter deve essere nel formato get<PropertyName>(). Ad esempio, se il nome della proprietà è foo, devi avere un metodo getFoo().
  • I metodi getter (se necessario) e setter della proprietà che stai animando devono operare sullo stesso tipo dei valori iniziale e finale specificati in ObjectAnimator. Ad esempio, devi avere targetObject.setPropName(float) e targetObject.getPropName() se crei il seguente ObjectAnimator:
    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    
  • A seconda della proprietà o dell'oggetto che stai animando, potresti dover chiamare il metodo invalidate() su una vista per forzare lo schermo a ridisegnarsi con i valori animati aggiornati. A tale scopo, utilizza il callback onAnimationUpdate(). Ad esempio, l'animazione della proprietà colore di un oggetto Drawable causa aggiornamenti allo schermo solo quando l'oggetto viene ridisegnato. Tutti i setter delle proprietà su View, come setAlpha() e setTranslationX(), invalidano correttamente la vista, quindi non è necessario invalidare la vista quando chiami questi metodi con nuovi valori. Per ulteriori informazioni sui listener, consulta la sezione sui Listener di animazione.

Crea una coreografia di più animazioni con un AnimatorSet

In molti casi, desideri riprodurre un'animazione che dipende dall'inizio o dalla fine di un'altra animazione. Il sistema Android ti consente di raggruppare le animazioni in un AnimatorSet, in modo da poter specificare se avviare le animazioni simultaneamente, in sequenza o dopo un determinato ritardo. Puoi anche nidificare AnimatorSet oggetti l'uno all'interno dell'altro.

Il seguente snippet di codice riproduce i seguenti oggetti Animator nel seguente modo:

  1. Riproduce bounceAnim.
  2. Riproduce squashAnim1, squashAnim2, stretchAnim1 e stretchAnim2 contemporaneamente.
  3. Riproduce bounceBackAnim.
  4. Riproduce fadeAnim.

Kotlin

val bouncer = AnimatorSet().apply {
    play(bounceAnim).before(squashAnim1)
    play(squashAnim1).with(squashAnim2)
    play(squashAnim1).with(stretchAnim1)
    play(squashAnim1).with(stretchAnim2)
    play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
}
AnimatorSet().apply {
    play(bouncer).before(fadeAnim)
    start()
}

Java

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();

Listener di animazione

Puoi ascoltare gli eventi importanti durante la durata di un'animazione con i listener descritti di seguito.

  • Animator.AnimatorListener
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate(): viene richiamato in ogni frame dell'animazione. Ascolta questo evento per utilizzare i valori calcolati generati da ValueAnimator durante un'animazione. Per utilizzare il valore, esegui una query sull'oggetto ValueAnimator passato nell'evento per ottenere il valore animato attuale con il metodo getAnimatedValue(). Se utilizzi ValueAnimator, è necessario implementare questo listener.

      A seconda della proprietà o dell'oggetto che stai animando, potresti dover richiamare invalidate() su una vista per forzare l'area dello schermo a ridisegnarsi con i nuovi valori animati. Ad esempio, l'animazione della proprietà colore di un oggetto Drawable causa gli aggiornamenti dello schermo solo quando l'oggetto viene ridisegnato. Tutti i setter delle proprietà sulla vista, come setAlpha() e setTranslationX(), invalidano correttamente la vista; di conseguenza, non è necessario invalidare la vista quando chiami questi metodi con nuovi valori.

Puoi estendere la classe AnimatorListenerAdapter anziché implementare l'interfaccia Animator.AnimatorListener, se non vuoi implementare tutti i metodi dell'interfaccia Animator.AnimatorListener. La classe AnimatorListenerAdapter fornisce implementazioni vuote dei metodi che puoi scegliere di sostituire.

Ad esempio, il seguente snippet di codice crea un AnimatorListenerAdapter solo per il callback onAnimationEnd():

Kotlin

ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            balls.remove((animation as ObjectAnimator).target)
        }
    })
}

Java

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
    balls.remove(((ObjectAnimator)animation).getTarget());
}

Anima le modifiche al layout degli oggetti ViewGroup

Il sistema di animazione delle proprietà consente di animare le modifiche agli oggetti ViewGroup e fornisce un modo semplice per animare gli oggetti View autonomamente.

Puoi animare le modifiche al layout all'interno di un ViewGroup con la classe LayoutTransition. Le visualizzazioni all'interno di un ViewGroup possono passare attraverso un'animazione che appare e scompare quando le aggiungi o le rimuovi da un ViewGroup o quando chiami il metodo setVisibility() di una vista con VISIBLE, INVISIBLE o GONE. Anche le viste rimanenti in ViewGroup possono animarsi nelle nuove posizioni quando aggiungi o rimuovi le viste. Puoi definire le seguenti animazioni in un oggetto LayoutTransition chiamando setAnimator() e passando un oggetto Animator con una delle seguenti costanti LayoutTransition:

  • APPEARING: un flag che indica l'animazione in esecuzione sugli elementi visualizzati nel contenitore.
  • CHANGE_APPEARING: un flag che indica l'animazione in esecuzione sugli elementi che vengono modificati a causa della visualizzazione di un nuovo elemento nel contenitore.
  • DISAPPEARING: un flag che indica l'animazione in esecuzione sugli elementi che stanno scomparendo dal contenitore.
  • CHANGE_DISAPPEARING: un flag che indica l'animazione in esecuzione sugli elementi che vengono modificati a causa della scomparsa di un elemento dal contenitore.

Puoi definire animazioni personalizzate per questi quattro tipi di eventi al fine di personalizzare l'aspetto delle transizioni di layout o semplicemente indicare al sistema di animazione di utilizzare le animazioni predefinite.

Per impostare l'attributo android:animateLayoutchanges su true per ViewGroup:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

L'impostazione di questo attributo su true anima automaticamente le viste aggiunte o rimosse dal ViewGroup, nonché dalle viste rimanenti in ViewGroup.

Animazione delle modifiche dello stato di visualizzazione utilizzando StateListAnimator

Il corso StateListAnimator ti consente di definire animatori che vengono eseguiti quando lo stato di una vista cambia. Questo oggetto si comporta come un wrapper per un oggetto Animator, chiamando l'animazione ogni volta che lo stato di visualizzazione specificato (ad esempio "premuto" o "attivata") cambia.

StateListAnimator può essere definito in una risorsa XML con un elemento <selector> principale ed elementi <item> secondari, ognuno dei quali specifica un diverso stato di visualizzazione definito dalla classe StateListAnimator. Ogni <item> contiene la definizione di un set di animazione della proprietà.

Ad esempio, il seguente file crea un animatore per l'elenco di stati che modifica la scala x e y della vista quando viene premuta:

res/xml/animate_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 150% -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100% -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

Per allegare l'animatore dell'elenco di stati a una vista, aggiungi l'attributo android:stateListAnimator come segue:

<Button android:stateListAnimator="@xml/animate_scale"
        ... />

Ora le animazioni definite in animate_scale.xml vengono utilizzate quando lo stato di questo pulsante cambia.

In alternativa, per assegnare un animatore dell'elenco di stati a una vista nel codice, utilizza il metodo AnimatorInflater.loadStateListAnimator() e assegna l'animatore alla vista con il metodo View.setStateListAnimator().

In alternativa, invece di animare le proprietà della vista, puoi riprodurre un'animazione disegnabile tra le modifiche di stato utilizzando AnimatedStateListDrawable. Alcuni widget di sistema in Android 5.0 usano queste animazioni per impostazione predefinita. L'esempio seguente mostra come definire un AnimatedStateListDrawable come risorsa XML:

<!-- res/drawable/myanimstatedrawable.xml -->
<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- provide a different drawable for each state-->
    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
        android:state_pressed="true"/>
    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
        android:state_focused="true"/>
    <item android:id="@id/default"
        android:drawable="@drawable/drawableD"/>

    <!-- specify a transition -->
    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="15" android:drawable="@drawable/dt1"/>
            <item android:duration="15" android:drawable="@drawable/dt2"/>
            ...
        </animation-list>
    </transition>
    ...
</animated-selector>

Usa un TypeEvaluator

Se vuoi animare un tipo sconosciuto al sistema Android, puoi creare un tuo strumento di valutazione implementando l'interfaccia TypeEvaluator. I tipi noti dal sistema Android sono int, float o un colore, supportati dai valutatori dei tipi IntEvaluator, FloatEvaluator e ArgbEvaluator.

Esiste un solo metodo da implementare nell'interfaccia TypeEvaluator: il metodo evaluate(). In questo modo, l'animatore che stai utilizzando può restituire un valore appropriato per la tua proprietà animata nel punto corrente dell'animazione. Il corso FloatEvaluator mostra come fare:

Kotlin

private class FloatEvaluator : TypeEvaluator<Any> {

    override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
        return (startValue as Number).toFloat().let { startFloat ->
            startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
        }
    }

}

Java

public class FloatEvaluator implements TypeEvaluator {

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

Nota: quando viene eseguito ValueAnimator (o ObjectAnimator), calcola una frazione attuale trascorsa dell'animazione (un valore compreso tra 0 e 1) e quindi ne calcola una versione interpolata in base all'interpolatore utilizzato. La frazione interpolata è quella che TypeEvaluator riceve tramite il parametro fraction, quindi non è necessario prendere in considerazione l'interpolatore quando calcoli i valori animati.

Utilizzare gli interpolatori

Un interpolatore definisce in che modo valori specifici in un'animazione vengono calcolati come funzione del tempo. Ad esempio, puoi specificare che le animazioni vengano visualizzate in modo lineare su tutta l'animazione, ovvero che l'animazione si sposti in modo uniforme per tutto il tempo, oppure puoi specificare animazioni per utilizzare un tempo non lineare, ad esempio utilizzando l'accelerazione o la decelerazione all'inizio o alla fine dell'animazione.

Gli interpolatori nel sistema di animazione ricevono una frazione dagli animatori che rappresentano il tempo trascorso dell'animazione. Gli interpolatori modificano questa frazione in modo che coincida con il tipo di animazione che mira a fornire. Il sistema Android fornisce un insieme di interpolatori comuni in android.view.animation package. Se nessuna di queste soluzioni soddisfa le tue esigenze, puoi implementare l'interfaccia TimeInterpolator e crearne una personalizzata.

Ad esempio, di seguito viene confrontato il modo in cui l'interpolatore predefinito AccelerateDecelerateInterpolator e LinearInterpolator calcolano le frazioni interpolate. Il valore LinearInterpolator non ha alcun effetto sulla frazione trascorsa. AccelerateDecelerateInterpolator accelera nell'animazione e ne rallenta l'esterno. I seguenti metodi definiscono la logica per questi interpolatori:

AccelerateDecelerateInterpolator

Kotlin

override fun getInterpolation(input: Float): Float =
        (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f

Java

@Override
public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

Kotlin

override fun getInterpolation(input: Float): Float = input

Java

@Override
public float getInterpolation(float input) {
    return input;
}

La seguente tabella rappresenta i valori approssimativi calcolati da questi interpolatori per un'animazione che dura 1000 ms:

ms trascorsi Frazione raggiunta/Frazione interpolata (Lineare) Frazione interpolata (accelerazione/decelerazione)
0 0 0
200 0,2 0,1
400 0,4 0,345
600 0,6 0,8
800 0,8 0,9
1000 1 1

Come mostra la tabella, LinearInterpolator cambia i valori alla stessa velocità, pari a 0,2 ogni 200 ms superati. AccelerateDecelerateInterpolator modifica i valori più velocemente di LinearInterpolator tra 200 ms e 600 ms e più lentamente tra 600 ms e 1000 ms.

Specificare i fotogrammi chiave

Un oggetto Keyframe è costituito da una coppia tempo/valore che consente di definire uno stato specifico in un momento specifico di un'animazione. Ogni fotogramma chiave può anche avere il proprio interpolatore per controllare il comportamento dell'animazione nell'intervallo tra il tempo del fotogramma chiave precedente e il tempo del fotogramma chiave.

Per creare un'istanza di un oggetto Keyframe, devi utilizzare uno dei metodi di fabbrica, ofInt(), ofFloat() o ofObject() per ottenere il tipo appropriato di Keyframe. Chiamerai quindi il metodo di fabbrica ofKeyframe() per ottenere un oggetto PropertyValuesHolder. Una volta creato l'oggetto, puoi ottenere un animatore passando l'oggetto PropertyValuesHolder e l'oggetto da animare. Il seguente snippet di codice mostra come eseguire questa operazione:

Kotlin

val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
    duration = 5000
}

Java

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000);

Anima le visualizzazioni

Il sistema di animazione delle proprietà consente un'animazione semplificata degli oggetti View e offre alcuni vantaggi rispetto al sistema di animazione delle proprietà. Il sistema di animazione della vista ha trasformato gli oggetti View modificando il modo in cui erano disegnati. Questa operazione veniva gestita nel contenitore di ogni vista, perché la vista stessa non aveva proprietà da manipolare. Di conseguenza, la visualizzazione è stata animata, ma non ha causato modifiche all'oggetto View stesso. Ciò ha portato a un comportamento come un oggetto ancora esistente nella sua posizione originale, anche se è stato disegnato in una posizione diversa sullo schermo. In Android 3.0, sono state aggiunte nuove proprietà e i corrispondenti metodi getter e setter per eliminare questo svantaggio.

Il sistema di animazione delle proprietà può animare le viste sullo schermo modificando le proprietà effettive negli oggetti View. Inoltre, le viste chiamano automaticamente anche il metodo invalidate() per aggiornare la schermata ogni volta che le relative proprietà vengono modificate. Le nuove proprietà nella classe View che facilitano le animazioni delle proprietà sono:

  • translationX e translationY: queste proprietà controllano la posizione della vista sotto forma di delta rispetto alle coordinate di sinistra e in alto, impostate dal relativo contenitore di layout.
  • rotation, rotationX e rotationY: queste proprietà controllano la rotazione in 2D (proprietà rotation) e in 3D intorno al punto pivot.
  • scaleX e scaleY: queste proprietà controllano il ridimensionamento 2D di una vista intorno al relativo punto pivot.
  • pivotX e pivotY: queste proprietà controllano la posizione del punto pivot attorno al quale avvengono le trasformazioni di rotazione e scalabilità. Per impostazione predefinita, il punto pivot si trova al centro dell'oggetto.
  • x e y: queste sono semplici proprietà di utilità per descrivere la posizione finale della vista nel relativo container, come somma dei valori sinistro e superiore e dei valori di translationX e translationY.
  • alpha: rappresenta la trasparenza alfa nella vista. Questo valore è 1 (opaco) per impostazione predefinita, con il valore 0 che rappresenta la trasparenza completa (non visibile).

Per animare una proprietà di un oggetto View, come il colore o il valore di rotazione, è sufficiente creare un animatore di proprietà e specificare la proprietà View da animare. Ecco alcuni esempi:

Kotlin

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)

Java

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

Per ulteriori informazioni sulla creazione di animatori, consulta le sezioni sull'animazione con ValueAnimator e ObjectAnimator.

Creazione di animazioni con ViewPropertyAnimator

ViewPropertyAnimator consente di animare facilmente diverse proprietà di un elemento View in parallelo, utilizzando un singolo oggetto Animator sottostante. Si comporta in modo molto simile a un ObjectAnimator, perché modifica i valori effettivi delle proprietà della vista, ma è più efficiente se si animano molte proprietà contemporaneamente. Inoltre, il codice per l'utilizzo di ViewPropertyAnimator è molto più conciso e più facile da leggere. I seguenti snippet di codice mostrano le differenze nell'utilizzo di più oggetti ObjectAnimator, un singolo ObjectAnimator e ViewPropertyAnimator quando si animano contemporaneamente le proprietà x e y di una vista.

Più oggetti ObjectAnimator

Kotlin

val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
    playTogether(animX, animY)
    start()
}

Java

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

Un ObjectAnimator

Kotlin

val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()

Java

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();

ViewPropertyAnimator

Kotlin

myView.animate().x(50f).y(100f)

Java

myView.animate().x(50f).y(100f);

Per informazioni più dettagliate su ViewPropertyAnimator, consulta il post del blog per gli sviluppatori Android corrispondente.

Dichiara le animazioni in XML

Il sistema di animazione delle proprietà ti consente di dichiarare le animazioni delle proprietà tramite XML anziché in modo programmatico. Se definisci le animazioni in XML, puoi riutilizzarle facilmente in più attività e modificare più facilmente la sequenza di animazione.

Per distinguere i file di animazione che utilizzano le nuove API di animazione delle proprietà da quelli che utilizzano il framework legacy di animazione della vista, a partire da Android 3.1, devi salvare i file XML per le animazioni delle proprietà nella directory res/animator/.

Le seguenti classi di animazione delle proprietà supportano la dichiarazione XML con i seguenti tag XML:

Per trovare gli attributi che puoi utilizzare nella dichiarazione XML, consulta Risorse dell'animazione. L'esempio seguente riproduce i due insiemi di animazioni di oggetti in sequenza, con il primo set nidificato che riproduce due animazioni di oggetti:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

Per eseguire questa animazione, devi aumentare in modo artificioso le risorse XML del tuo codice impostandole su un oggetto AnimatorSet, quindi impostare gli oggetti di destinazione per tutte le animazioni prima di iniziare il set di animazioni. La chiamata a setTarget() consente di impostare un singolo oggetto di destinazione per tutti gli elementi secondari di AnimatorSet per comodità. Il seguente codice mostra come fare:

Kotlin

(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
    setTarget(myObject)
    start()
}

Java

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animator.property_animator);
set.setTarget(myObject);
set.start();

Puoi anche dichiarare un ValueAnimator in XML, come mostrato nell'esempio seguente:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

Per utilizzare l'oggetto ValueAnimator precedente nel codice, devi gonfiare l'oggetto, aggiungere un AnimatorUpdateListener, ottenere il valore dell'animazione aggiornato e utilizzarlo in una proprietà di una delle viste, come illustrato nel seguente codice:

Kotlin

(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
    addUpdateListener { updatedAnimation ->
        textView.translationX = updatedAnimation.animatedValue as Float
    }

    start()
}

Java

ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
        R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

xmlAnimator.start();

Per informazioni sulla sintassi XML per la definizione delle animazioni delle proprietà, consulta Risorse dell'animazione .

Potenziali effetti sulle prestazioni dell'interfaccia utente

Gli animatori che aggiornano l'interfaccia utente comportano un lavoro di rendering aggiuntivo per ogni frame in cui viene eseguita l'animazione. Per questo motivo, l'utilizzo di animazioni che consumano molte risorse può influire negativamente sulle prestazioni dell'app.

Il lavoro necessario per animare la UI viene aggiunto alla fase di animazione della pipeline di rendering. Puoi scoprire se le animazioni influiscono sulle prestazioni della tua app attivando il Rendering GPU del profilo e monitorando la fase dell'animazione. Per ulteriori informazioni, consulta la procedura dettagliata di rendering delle GPU del profilo.