Panoramica dell'animazione della proprietà

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

Il sistema di animazione delle proprietà è un framework solido che ti consente di animare quasi tutto. Puoi definire un'animazione per modificare qualsiasi proprietà dell'oggetto nel tempo, indipendentemente dal fatto che venga visualizzata sullo schermo o meno. Un'animazione di proprietà modifica il valore di una proprietà (un campo in un oggetto) per un periodo di tempo specificato. Per animare un elemento, devi specificare la proprietà dell'oggetto che vuoi animare, ad esempio la posizione dell'oggetto sullo schermo, la durata dell'animazione e i valori tra cui 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 durata predefinita è 300 ms.
  • Interpolazione temporale: puoi specificare come vengono calcolati i valori della proprietà in funzione del tempo trascorso corrente dell'animazione.
  • Conteggio e comportamento delle ripetizioni: puoi specificare se un'animazione deve ripetersi quando raggiunge la fine di una durata e quante volte deve ripetersi. Puoi anche specificare se vuoi che l'animazione venga riprodotta al contrario. Se lo imposti su Inverti, l'animazione viene riprodotta in avanti e poi all'indietro ripetutamente, finché non viene raggiunto il numero di ripetizioni.
  • Set di animazioni: puoi raggruppare le animazioni in set logici che vengono riprodotti insieme o in sequenza o dopo ritardi specificati.
  • Ritardo aggiornamento fotogrammi: puoi specificare la frequenza di aggiornamento dei fotogrammi dell'animazione. Il valore predefinito è impostato per l'aggiornamento ogni 10 ms, ma la velocità con cui l'applicazione può aggiornare i frame dipende in ultima analisi dal carico complessivo del sistema e dalla velocità con cui il sistema può gestire il timer sottostante.

Per vedere un esempio completo di animazione delle proprietà, consulta la classe ChangeColor nell'esempio CustomTransition su GitHub.

Come funziona l'animazione delle proprietà

Per prima cosa, vediamo come funziona un'animazione con un semplice esempio. La figura 1 mostra un oggetto ipotetico animato con la proprietà x, che rappresenta la sua posizione orizzontale su una schermata. La durata dell'animazione è impostata su 40 ms e la distanza da percorrere è di 40 pixel. Ogni 10 ms, ovvero la frequenza di aggiornamento dei frame predefinita, l'oggetto si sposta orizzontalmente di 10 pixel. Al termine di 40 ms, l'animazione si interrompe e l'oggetto si ferma nella posizione orizzontale 40. Questo è un esempio di animazione con interpolazione lineare, il che significa che l'oggetto si muove a velocità costante.

Figura 1. Esempio di animazione lineare

Puoi anche specificare che le animazioni abbiano un'interpolazione non lineare. La figura 2 illustra un oggetto ipotetico che accelera all'inizio dell'animazione e decelera 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 a metà percorso, poi decelera da metà percorso fino alla fine dell'animazione. Come mostrato nella figura 2, la distanza percorsa all'inizio e alla fine dell'animazione è inferiore rispetto a quella percorsa nella fase intermedia.

Figura 2. Esempio di animazione non lineare

Esaminiamo nel dettaglio come i componenti importanti del sistema di animazione delle proprietà calcolerebbero animazioni come quelle illustrate sopra. La Figura 3 mostra come interagiscono le classi principali.

Figura 3. Come vengono calcolate le animazioni

L'oggetto ValueAnimator tiene traccia della durata dell'animazione, ad esempio per quanto tempo è stata eseguita e il valore corrente della proprietà che sta animando.

ValueAnimator racchiude un TimeInterpolator, che definisce l'interpolazione dell'animazione, e un TypeEvaluator, che definisce come calcolare i valori per la proprietà animata. Ad esempio, nella figura 2, l'TimeInterpolator utilizzato sarebbe AccelerateDecelerateInterpolator e l'TypeEvaluator sarebbe IntEvaluator.

Per avviare un'animazione, crea un ValueAnimator e fornisci i valori iniziale e finale della proprietà da animare, insieme alla durata dell'animazione. Quando chiami il numero start(), l'animazione inizia. 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 in cui l'animazione è stata completata, 0 significa 0% e 1 significa 100%. Ad esempio, nella Figura 1, la frazione trascorsa a t = 10 ms sarebbe pari a 0,25 perché la durata totale è t = 40 ms.

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

Quando viene calcolata la frazione interpolata, ValueAnimator chiama il 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 a t = 10 ms, quindi il valore della proprietà in quel momento sarebbe 0,15 × (40 - 0), ovvero 6.

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

Il sistema di animazione delle visualizzazioni consente di animare solo gli oggetti View, quindi se vuoi animare oggetti non View, devi implementare il tuo codice. Il sistema di animazione delle visualizzazioni è anche limitato dal fatto che espone solo alcuni aspetti di un oggetto View da animare, come il ridimensionamento e la rotazione di una visualizzazione, ma non il colore di sfondo, ad esempio.

Un altro svantaggio del sistema di animazione delle visualizzazioni è che modificava solo la posizione in cui veniva disegnata la visualizzazione e non la visualizzazione stessa. Ad esempio, se hai animato un pulsante per spostarlo sullo schermo, il pulsante viene disegnato correttamente, ma la posizione effettiva in cui puoi fare clic sul pulsante non cambia, quindi devi implementare la tua logica per gestire questo problema.

Con il sistema di animazione delle proprietà, questi vincoli vengono completamente rimossi e puoi animare qualsiasi proprietà di qualsiasi oggetto (viste e non viste) e l'oggetto stesso viene effettivamente modificato. Il sistema di animazione delle proprietà è anche più solido nel modo in cui esegue l'animazione. A livello generale, assegni gli 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 delle visualizzazioni, tuttavia, richiede meno tempo per la configurazione e meno codice da scrivere. Se l'animazione della visualizzazione esegue tutto ciò che devi fare o se il codice esistente funziona già come vuoi, non è necessario utilizzare il sistema di animazione delle proprietà. Potrebbe anche avere senso utilizzare entrambi i sistemi di animazione per situazioni diverse, se si presenta 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 visualizzazioni definisce già molti interpolatori in android.view.animation, puoi utilizzare questi interpolatori anche nel sistema di animazione delle proprietà. Le tabelle seguenti descrivono i componenti principali del sistema di animazione delle proprietà.

La classe Animator fornisce la struttura di base per la creazione di animazioni. In genere non si utilizza questa classe direttamente perché fornisce solo funzionalità minime che devono essere estese per supportare completamente l'animazione dei valori. 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 della proprietà da animare. Contiene tutte le funzionalità di base che calcolano i valori dell'animazione e i dettagli di temporizzazione di ogni animazione, informazioni sull'eventuale ripetizione di un'animazione, listener che ricevono eventi di aggiornamento e la possibilità di impostare tipi personalizzati da valutare. L'animazione delle proprietà è composta da due parti: il calcolo dei valori animati e l'impostazione di questi valori sull'oggetto e sulla proprietà animati. ValueAnimator non esegue la seconda parte, quindi devi ascoltare gli aggiornamenti dei valori calcolati da ValueAnimator e modificare gli oggetti che vuoi animare con la tua logica. Per ulteriori informazioni, consulta la sezione Animazione con ValueAnimator.
ObjectAnimator Una sottoclasse di ValueAnimator che ti consente di impostare un oggetto di destinazione e una proprietà dell'oggetto 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é semplifica notevolmente il processo di animazione dei valori sugli oggetti target. Tuttavia, a volte vuoi utilizzare ValueAnimator direttamente perché ObjectAnimator ha alcune limitazioni in più, ad esempio richiede la presenza di metodi di accesso specifici nell'oggetto di destinazione.
AnimatorSet Fornisce un meccanismo per raggruppare le animazioni in modo che vengano eseguite in relazione tra loro. Puoi impostare le animazioni in modo che vengano riprodotte insieme, in sequenza o dopo un ritardo specificato. Per saperne di più, consulta la sezione Coreografia di più animazioni con i 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, il valore iniziale e finale 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 L'evaluator predefinito per calcolare i valori delle proprietà int.
FloatEvaluator L'evaluator predefinito per calcolare i valori delle proprietà float.
ArgbEvaluator L'evaluator predefinito per calcolare i valori delle proprietà di colore rappresentate come valori esadecimali.
TypeEvaluator Un'interfaccia che ti consente di creare il tuo valutatore. Se stai animando una proprietà dell'oggetto che non è un int, un float o un colore, devi implementare l'interfaccia TypeEvaluator per specificare come calcolare i valori animati della proprietà dell'oggetto. Puoi anche specificare un TypeEvaluator personalizzato per i valori di int, float e colore se vuoi elaborare questi tipi in modo diverso dal comportamento predefinito. Per ulteriori informazioni su come scrivere un valutatore personalizzato, consulta la sezione Utilizzo di un TypeEvaluator.

Un interpolatore temporale definisce come vengono calcolati valori specifici in un'animazione in funzione del tempo. Ad esempio, puoi specificare che le animazioni avvengano in modo lineare per tutta l'animazione, il che significa che l'animazione si muove in modo uniforme per tutto il tempo, oppure puoi specificare che le animazioni utilizzino un tempo non lineare, ad esempio accelerando all'inizio e decelerando 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 crea il tuo interpolatore. 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 velocità di variazione inizia e termina lentamente, ma accelera nella fase intermedia.
AccelerateInterpolator Un interpolatore la cui velocità di variazione inizia lentamente e poi accelera.
AnticipateInterpolator Un interpolatore la cui modifica inizia all'indietro e poi si sposta in avanti.
AnticipateOvershootInterpolator Un interpolatore la cui modifica inizia all'indietro, si sposta in avanti e supera il valore di destinazione, per poi tornare al valore finale.
BounceInterpolator Un interpolatore la cui variazione rimbalza alla fine.
CycleInterpolator Un interpolatore la cui animazione si ripete per un numero specificato di cicli.
DecelerateInterpolator Un interpolatore la cui velocità di variazione inizia rapidamente e poi rallenta.
LinearInterpolator Un interpolatore il cui tasso di variazione è costante.
OvershootInterpolator Un interpolatore la cui variazione si sposta in avanti e supera l'ultimo valore, per poi tornare indietro.
TimeInterpolator Un'interfaccia che ti consente di implementare il tuo interpolatore.

Animare utilizzando ValueAnimator

La classe ValueAnimator consente di animare i valori di un determinato tipo per la durata di un'animazione specificando un insieme di valori int, float o di colore da animare. Ottieni un ValueAnimator chiamando uno dei suoi metodi di fabbrica: ofInt(), ofFloat() o ofObject(). Ad esempio:

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, tra 0 e 100, per una durata di 1000 ms, quando viene eseguito il metodo start().

Puoi anche specificare un tipo personalizzato da animare 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 un AnimatorUpdateListener all'oggetto ValueAnimator, come mostrato nel codice seguente:

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 dell'animazione aggiornato e utilizzarlo in una proprietà di una delle tue viste. Per saperne di più sui listener, consulta la sezione relativa ai listener di animazione.

Animare utilizzando ObjectAnimator

ObjectAnimator è una sottoclasse di ValueAnimator (descritta nella sezione precedente) e combina il motore di temporizzazione e il calcolo del valore di ValueAnimator con la possibilità di animare una proprietà denominata di un oggetto di destinazione. In questo modo, animare qualsiasi oggetto è molto più semplice, in quanto non è più necessario implementare ValueAnimator.AnimatorUpdateListener, perché la proprietà animata si aggiorna automaticamente.

L'istanza di un ObjectAnimator è simile a un ValueAnimator, ma devi specificare anche l'oggetto e il nome della proprietà dell'oggetto (come stringa) insieme ai valori tra cui animare:

Kotlin

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

Java

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

Per aggiornare correttamente le proprietà di ObjectAnimator, devi:

  • La proprietà dell'oggetto che stai animando deve avere una funzione setter (in formato camel case) nella forma set<PropertyName>(). Poiché ObjectAnimator aggiorna automaticamente la proprietà durante l'animazione, deve essere in grado di accedere alla proprietà con questo metodo setter. Ad esempio, se il nome della proprietà è foo, devi avere un metodo setFoo(). Se questo metodo setter non esiste, hai tre opzioni:
    • Aggiungi il metodo setter alla classe se disponi dei diritti necessari per farlo.
    • Utilizza una classe wrapper su cui hai i diritti di modifica e fai in modo che riceva il valore con un metodo setter valido e lo inoltri all'oggetto originale.
    • Usa invece 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 che viene 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 che specifichi per 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, potrebbe essere necessario chiamare il metodo invalidate() su una visualizzazione per forzare il ridisegno dello schermo con i valori animati aggiornati. Esegui questa operazione nel callback onAnimationUpdate(). Ad esempio, l'animazione della proprietà di colore di un oggetto Drawable causa aggiornamenti allo schermo solo quando l'oggetto si ridisegna. Tutti i setter di proprietà in View, ad esempio setAlpha() e setTranslationX() invalidano correttamente la visualizzazione, quindi non è necessario invalidarla quando chiami questi metodi con nuovi valori. Per saperne di più sui listener, consulta la sezione Listener di animazione.

Coreografare più animazioni utilizzando un AnimatorSet

In molti casi, vuoi 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 contemporaneamente, in sequenza o dopo un ritardo specificato. Puoi anche nidificare gli oggetti AnimatorSet l'uno dentro l'altro.

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

  1. Tempo rimanente: bounceAnim.
  2. Riproduce squashAnim1, squashAnim2, stretchAnim1 e stretchAnim2 contemporaneamente.
  3. Tempo rimanente: bounceBackAnim.
  4. Tempo rimanente: 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

Con i listener descritti di seguito, puoi ascoltare gli eventi importanti durante la durata di un'animazione.

  • Animator.AnimatorListener
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate(): chiamato su ogni fotogramma 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 all'evento per ottenere il valore animato corrente con il metodo getAnimatedValue(). L'implementazione di questo listener è obbligatoria se utilizzi ValueAnimator.

      A seconda della proprietà o dell'oggetto che stai animando, potresti dover chiamare invalidate() su una visualizzazione per forzare il ridisegno di quell'area dello schermo con i nuovi valori animati. Ad esempio, l'animazione della proprietà color di un oggetto Drawable causa aggiornamenti allo schermo solo quando l'oggetto si ridisegna. Tutti i setter di proprietà in View, come setAlpha() e setTranslationX() invalidano correttamente View, quindi non è necessario invalidare View 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 eseguire l'override.

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());
}

Animare le modifiche al layout degli oggetti ViewGroup

Il sistema di animazione delle proprietà consente di animare le modifiche agli oggetti ViewGroup e di animare facilmente gli oggetti View stessi.

Puoi animare le modifiche al layout all'interno di un ViewGroup con la classe LayoutTransition. Le visualizzazioni all'interno di un ViewGroup possono essere animate in modo che appaiano e scompaiano quando le aggiungi o le rimuovi da un ViewGroup o quando chiami il metodo setVisibility() di una View con VISIBLE, INVISIBLE o GONE. Anche le visualizzazioni rimanenti nel ViewGroup possono animarsi nelle loro nuove posizioni quando aggiungi o rimuovi visualizzazioni. 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 eseguita sugli elementi che vengono visualizzati nel contenitore.
  • CHANGE_APPEARING: un flag che indica l'animazione eseguita sugli elementi che cambiano a causa della visualizzazione di un nuovo elemento nel contenitore.
  • DISAPPEARING: un flag che indica l'animazione eseguita sugli elementi che scompaiono dal contenitore.
  • CHANGE_DISAPPEARING: un flag che indica l'animazione eseguita sugli elementi che stanno cambiando perché un elemento scompare dal contenitore.

Puoi definire le tue animazioni personalizzate per questi quattro tipi di eventi per personalizzare l'aspetto delle transizioni del 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" />

Se imposti questo attributo su true, le visualizzazioni aggiunte o rimosse dal ViewGroup, nonché le visualizzazioni rimanenti nel ViewGroup, vengono animate automaticamente.

Animare le modifiche dello stato della visualizzazione utilizzando StateListAnimator

La classe StateListAnimator ti consente di definire gli animatori che vengono eseguiti quando cambia lo stato di una visualizzazione. Questo oggetto funge da wrapper per un oggetto Animator, richiamando l'animazione ogni volta che cambia lo stato della visualizzazione specificato (ad esempio"premuto " o"con focus).

StateListAnimator può essere definito in una risorsa XML con un elemento radice <selector> ed elementi secondari <item> che specificano ciascuno un diverso stato della visualizzazione definito dalla classe StateListAnimator. Ogni <item> contiene la definizione di un insieme di animazioni delle proprietà.

Ad esempio, il seguente file crea un animatore di elenco di stati che modifica la scala x e y della visualizzazione 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 collegare l'animatore dell'elenco degli stati a una visualizzazione, aggiungi l'attributo android:stateListAnimator nel seguente modo:

<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 di elenchi di stati a una visualizzazione nel codice, utilizza il metodo AnimatorInflater.loadStateListAnimator() e assegna l'animatore alla visualizzazione con il metodo View.setStateListAnimator().

In alternativa, anziché animare le proprietà della visualizzazione, puoi riprodurre un'animazione disegnabile tra i cambi di stato utilizzando AnimatedStateListDrawable. Alcuni widget di sistema in Android 5.0 utilizzano 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>

Utilizzare un TypeEvaluator

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

Nell'interfaccia TypeEvaluator esiste un solo metodo di implementazione, il metodo evaluate(). In questo modo, l'animatore che utilizzi restituisce un valore appropriato per la proprietà animata nel punto corrente dell'animazione. La classe 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), viene calcolata una frazione trascorsa corrente dell'animazione (un valore compreso tra 0 e 1), quindi viene calcolata una versione interpolata di questa frazione a seconda dell'interpolatore che stai utilizzando. La frazione interpolata è ciò che TypeEvaluator riceve tramite il parametro fraction, quindi non devi tenere conto dell'interpolatore quando calcoli i valori animati.

Utilizzare gli interpolatori

Un interpolatore definisce come vengono calcolati valori specifici in un'animazione in funzione del tempo. Ad esempio, puoi specificare che le animazioni avvengano in modo lineare nell'intera animazione, il che significa che l'animazione si muove in modo uniforme per tutto il tempo, oppure puoi specificare che le animazioni utilizzino 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 rappresenta il tempo trascorso dell'animazione. Gli interpolatori modificano questa frazione in modo che coincida con il tipo di animazione che intendono fornire. Il sistema Android fornisce un insieme di interpolatori comuni in android.view.animation package. Se nessuno di questi soddisfa le tue esigenze, puoi implementare l'interfaccia TimeInterpolator e creare il tuo.

Di seguito viene confrontato il modo in cui l'interpolatore predefinito AccelerateDecelerateInterpolator e LinearInterpolator calcolano le frazioni interpolate. LinearInterpolator non ha alcun effetto sulla frazione trascorsa. AccelerateDecelerateInterpolator accelera nell'animazione e decelera all'uscita. I seguenti metodi definiscono la logica di 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 tabella seguente mostra i valori approssimativi calcolati da questi interpolatori per un'animazione che dura 1000 ms:

ms elapsed Frazione trascorsa/Frazione interpolata (lineare) Frazione interpolata (accelera/rallenta)
0 0 0
200 0,2 0,1
400 .4 0,345
600 .6 0,654
800 0,8 .9
1000 1 1

Come mostra la tabella, LinearInterpolator cambia i valori alla stessa velocità, 0,2 ogni 200 ms. 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 ora/valore che ti consente di definire uno stato specifico in un momento specifico di un'animazione. Ogni fotogramma chiave può anche avere un proprio interpolatore per controllare il comportamento dell'animazione nell'intervallo tra l'ora del fotogramma chiave precedente e l'ora di questo 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. A questo punto, chiama il metodo di fabbrica ofKeyframe() per ottenere un oggetto PropertyValuesHolder. Una volta ottenuto 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);

Animare le visualizzazioni

Il sistema di animazione delle proprietà consente di animare in modo semplificato gli oggetti View e offre alcuni vantaggi rispetto al sistema di animazione delle visualizzazioni. Il sistema di animazione delle visualizzazioni ha trasformato gli oggetti View modificando il modo in cui venivano disegnati. Questo è stato gestito nel contenitore di ogni visualizzazione, perché la visualizzazione stessa non aveva proprietà da manipolare. Di conseguenza, la visualizzazione è stata animata, ma non è stata apportata alcuna modifica all'oggetto View stesso. Ciò ha portato a comportamenti come un oggetto che esiste ancora 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 metodi getter e setter corrispondenti per eliminare questo inconveniente.

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

  • translationX e translationY: queste proprietà controllano la posizione della vista come delta rispetto alle coordinate sinistra e superiore impostate dal contenitore del layout.
  • rotation, rotationX e rotationY: queste proprietà controllano la rotazione in 2D (proprietà rotation) e in 3D intorno al punto di rotazione.
  • scaleX e scaleY: queste proprietà controllano la scalabilità 2D di una vista intorno al suo punto di pivot.
  • pivotX e pivotY: queste proprietà controllano la posizione del punto pivot, attorno al quale si verificano le trasformazioni di rotazione e scalabilità. Per impostazione predefinita, il punto di rotazione si trova al centro dell'oggetto.
  • x e y: si tratta di semplici proprietà di utilità per descrivere la posizione finale della visualizzazione nel relativo contenitore, come somma dei valori left e top e dei valori translationX e translationY.
  • alpha: rappresenta la trasparenza alfa nella visualizzazione. Il valore predefinito è 1 (opaco), mentre un valore pari a 0 indica la completa trasparenza (non visibile).

Per animare una proprietà di un oggetto View, ad esempio il colore o il valore di rotazione, devi solo creare un animatore di proprietà e specificare la proprietà View che vuoi animare. Ad esempio:

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.

Creare animazioni utilizzando ViewPropertyAnimator

ViewPropertyAnimator offre un modo semplice per animare diverse proprietà di un 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 visualizzazione, ma è più efficiente quando anima molte proprietà contemporaneamente. Inoltre, il codice per l'utilizzo di ViewPropertyAnimator è molto più conciso e facile da leggere. Gli snippet di codice riportati di seguito mostrano le differenze nell'utilizzo di più oggetti ObjectAnimator, di un singolo ObjectAnimator e di ViewPropertyAnimator durante l'animazione simultanea delle proprietà x e y di una visualizzazione.

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 sviluppatori Android corrispondente.

Dichiarare le animazioni in XML

Il sistema di animazione delle proprietà consente di dichiarare le animazioni delle proprietà con XML anziché in modo programmatico. Definendo 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 di animazione delle visualizzazioni legacy, 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 di animazione. L'esempio seguente riproduce in sequenza i due set di animazioni degli oggetti, con il primo set nidificato che riproduce due animazioni degli oggetti insieme:

<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 gonfiare le risorse XML nel codice in un oggetto AnimatorSet e poi impostare gli oggetti di destinazione per tutte le animazioni prima di avviare il set di animazioni. La chiamata di setTarget() imposta un singolo oggetto di destinazione per tutti gli elementi secondari di AnimatorSet per comodità. Il seguente codice mostra come eseguire questa operazione:

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 il valore ValueAnimator precedente nel codice, devi gonfiare l'oggetto, aggiungere un AnimatorUpdateListener, ottenere il valore di animazione aggiornato e utilizzarlo in una proprietà di una delle tue visualizzazioni, come mostrato nel codice seguente:

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 definire le animazioni delle proprietà, consulta Risorse di animazione .

Potenziali effetti sulle prestazioni dell'interfaccia utente

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

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