Panoramica dell'animazione della proprietà

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

Il sistema di animazione delle proprietà è un framework efficace che consente di animare quasi tutto. Puoi definire un'animazione per modificare qualsiasi proprietà dell'oggetto nel tempo, indipendentemente dal fatto che venga visualizzata o meno sullo schermo. L'animazione di una 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 i quali eseguire l'animazione.

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 i valori della proprietà vengono calcolati come funzione del tempo trascorso attuale dell'animazione.
  • Conteggio e comportamento ripetuti: puoi specificare se un'animazione deve essere ripetuta o meno quando raggiunge la fine di una durata e il numero di volte in cui ripeterla. Puoi anche specificare se vuoi che l'animazione venga riprodotta in senso inverso. Impostando l'opzione sul retro, l'animazione viene riprodotta ripetutamente, avanti e indietro, fino al raggiungimento del numero di ripetizioni.
  • Set di animazioni: puoi raggruppare le animazioni in set logici che vengono riprodotti insieme, in sequenza o dopo specifici ritardi.
  • Ritardo aggiornamento frame: puoi specificare la frequenza di aggiornamento dei frame dell'animazione. Il valore predefinito è impostato sull'aggiornamento ogni 10 ms, ma la velocità con cui l'applicazione può aggiornare i frame dipende in definitiva dal livello di occupazione generale del sistema e dalla velocità con cui il sistema è in grado di gestire il timer sottostante.

Per un esempio completo dell'animazione delle proprietà, consulta la classe ChangeColor nell'esempio CustomTransizione su GitHub.

Come funziona l'animazione della proprietà

Per prima cosa, 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 dei frame, l'oggetto si sposta orizzontalmente di 10 pixel. Dopo 40 ms, l'animazione si interrompe e l'oggetto termina in posizione orizzontale 40. Questo è un esempio di animazione con interpolazione lineare, nel senso 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 mostra un ipotetico oggetto che accelera all'inizio dell'animazione e declina al termine 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 a metà strada e poi deceleziona dal punto a metà strada fino alla fine. 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

Diamo un'occhiata in dettaglio a come i componenti importanti del sistema di animazione delle proprietà calcolano animazioni come quelle illustrate sopra. La Figura 3 mostra il funzionamento delle classi principali tra loro.

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 sta animando.

ValueAnimator contiene 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 corrisponde a IntEvaluator.

Per avviare un'animazione, crea un elemento ValueAnimator e assegnagli i valori iniziale e finale per la proprietà che vuoi animare, insieme alla durata dell'animazione. Quando chiami start(), inizia 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 in cui l'animazione è stata completata, 0 indica 0% e 1 indica 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 il calcolo di una frazione trascorsa, chiama la risorsa TimeInterpolator attualmente impostata 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, 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 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 a t = 10 ms, quindi il valore della proprietà in quel momento sarebbe 0,15 × (40 - 0) o 6.

Differenze tra l'animazione della proprietà e quella della vista

Il sistema di animazione visualizzazione offre la possibilità di animare solo oggetti View. Se vuoi animare oggetti non View, devi implementare un codice personalizzato. Il sistema di animazione visualizzazione è inoltre vincolato dal fatto che mostra solo alcuni aspetti di un oggetto View da animare, come la scalabilità e la rotazione di una vista, ma non il colore di sfondo, ad esempio.

Un altro svantaggio del sistema di animazione delle visualizzazioni è che modifica solo dove è stata tracciata la vista e non la vista effettiva. Ad esempio, se hai animato un pulsante per spostarlo sullo schermo, questo viene disegnato correttamente, ma la posizione effettiva in cui puoi fare clic non cambia, quindi dovrai implementare una logica personalizzata per gestire questa situazione.

Con il sistema di animazione delle proprietà, questi vincoli vengono rimossi completamente e puoi animare qualsiasi proprietà di qualsiasi oggetto (elementi visti e non) e l'oggetto stesso viene in realtà modificato. Il sistema di animazione delle proprietà è anche più efficace per quanto riguarda le animazioni. A un livello generale, assegni animatori alle proprietà che vuoi animare, come colore, posizione o dimensioni, e puoi definire aspetti dell'animazione come l'interpolazione e la sincronizzazione di più animatori.

Il sistema di visualizzazione dell'animazione, tuttavia, richiede meno tempo per la configurazione e meno codice per la scrittura. Se l'animazione di visualizzazione completa tutte le operazioni necessarie o se il codice esistente funziona già come previsto, non è necessario utilizzare il sistema di animazione delle proprietà. Potrebbe inoltre avere senso utilizzare entrambi i sistemi di animazione per situazioni diverse, qualora si verifichi 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 della visualizzazione 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 di animazioni. In genere non utilizzi questa classe direttamente perché offre 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. Include tutte le funzionalità di base per il calcolo dei valori dell'animazione e contiene i dettagli dei tempi di ogni animazione, le informazioni sulla ripetizione di un'animazione, i listener che ricevono eventi di aggiornamento e la possibilità di impostare tipi personalizzati da valutare. L'animazione delle proprietà prevede due parti: il calcolo dei valori animati e l'impostazione di tali valori nell'oggetto e nella proprietà che vengono animati. ValueAnimator non esegue la seconda parte, quindi devi rimanere in ascolto degli aggiornamenti ai 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 ValueAnimator che consente di impostare un oggetto e una proprietà 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é semplifica molto il processo di animazione dei valori negli oggetti target. Tuttavia, a volte potresti voler utilizzare ValueAnimator direttamente perché ObjectAnimator ha alcune altre limitazioni, come la richiesta della presenza di metodi della funzione di accesso specifici nell'oggetto di destinazione.
AnimatorSet Fornisce un meccanismo per raggruppare le animazioni in modo che vengano eseguite l'una in relazione 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 Coreografia di più animazioni con insiemi di animazioni.

I valutatori indicano al sistema di animazione delle proprietà come calcolare i valori per una determinata proprietà. Prendono i dati di tempo forniti da una classe Animator, il valore 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 delle proprietà int.
FloatEvaluator Il valutatore predefinito per calcolare i valori delle proprietà float.
ArgbEvaluator Il valutatore predefinito per calcolare i valori per le proprietà dei colori rappresentate come valori esadecimali.
TypeEvaluator Un'interfaccia che consente di creare un valutatore personalizzato. Se stai animando una proprietà dell'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 TypeEvaluator personalizzato per int, float e i valori di colore, se vuoi elaborare questi tipi in modo diverso rispetto al comportamento predefinito. Per ulteriori informazioni su come scrivere una valutazione personalizzata, consulta la sezione Utilizzo di un valutatore dei tipi.

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

Tabella 3. Interpolatori

Classe/interfaccia Descrizione
AccelerateDecelerateInterpolator Un interpolatore la cui velocità di cambiamento inizia e termina lentamente, ma accelera al centro.
AccelerateInterpolator Un interpolatore il cui ritmo di cambiamento inizia lentamente e poi accelera.
AnticipateInterpolator Un interpolatore il cui cambiamento inizia indietro e poi si muove in avanti.
AnticipateOvershootInterpolator Un interpolatore la cui variazione inizia indietro, scorre in avanti e supera il valore target, per poi tornare al valore finale.
BounceInterpolator Un interpolatore il cui cambiamento 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 il cui cambiamento va in avanti e supera l'ultimo valore, poi torna indietro.
TimeInterpolator Un'interfaccia che consente di implementare il tuo interpolatore.

Creare animazioni con ValueAnimator

La classe ValueAnimator consente di animare i valori di qualche tipo per la durata di un'animazione specificando un insieme di valori di int, float o di colore. 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, l'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 un 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 dell'animazione aggiornato e utilizzarlo in una proprietà di una delle tue viste. Per ulteriori informazioni sui listener, consulta la sezione relativa ai ascoltatori di animazioni.

Creare animazioni con ObjectAnimator

ObjectAnimator è una sottoclasse di ValueAnimator (discussa nella sezione precedente) e combina il motore dei tempi e il calcolo del valore di ValueAnimator con la possibilità di animare una proprietà denominata di un oggetto di destinazione. Questo semplifica notevolmente l'animazione degli oggetti, poiché non è più necessario implementare ValueAnimator.AnimatorUpdateListener poiché la proprietà animata si aggiorna automaticamente.

La creazione di un'istanza ObjectAnimator è simile a una ValueAnimator, ma devi anche specificare l'oggetto e il nome della proprietà dell'oggetto (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 aggiornare correttamente le proprietà di ObjectAnimator, devi:

  • 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 il metodo setter non esiste, hai tre opzioni:
    • Aggiungi il metodo setter alla classe se disponi dei diritti necessari.
    • Utilizza una classe wrapper che disponi dei diritti di modifica e fai in modo che quel wrapper riceva il valore con un metodo 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. Di conseguenza, 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 iniziali e finali 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 la visualizzazione della schermata stessa con i valori animati aggiornati. Puoi farlo nel callback onAnimationUpdate(). Ad esempio, l'animazione della proprietà del colore di un oggetto Drawable causa aggiornamenti della schermata solo quando l'oggetto viene ridisegnato. Tutti i responsabili dell'impostazione delle proprietà nella vista, 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 relativa ai ascoltatori di animazioni.

Coreografia di più animazioni con un AnimatorSet

In molti casi, vuoi riprodurre un'animazione che dipende da quando inizia o termina un'altra animazione. Il sistema Android consente di raggruppare le animazioni in un elemento AnimatorSet, così puoi specificare se avviare le animazioni contemporaneamente, in sequenza o dopo un determinato ritardo. Puoi anche nidificare AnimatorSet oggetti l'uno all'interno dell'altro.

Lo snippet di codice riportato di seguito 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 rimanere in ascolto di eventi importanti durante la durata di un'animazione con i listener descritti di seguito.

  • Animator.AnimatorListener
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate(): richiamato a 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 all'evento per ottenere l'attuale valore animato 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 vista per forzare l'area dello schermo a ridisegnarsi con i nuovi valori animati. Ad esempio, l'animazione della proprietà del colore di un oggetto Drawable causa aggiornamenti della schermata solo quando l'oggetto viene ridisegnato. Tutti i responsabili dell'impostazione 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.

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

Animazione delle modifiche al layout degli oggetti ViewGroup

Il sistema di animazione delle proprietà consente di animare le modifiche apportate agli oggetti ViewGroup, nonché un modo semplice per animare 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 soggette a un'animazione che appare o scompare quando le aggiungi o le rimuovi da un ViewGroup o quando chiami il metodo setVisibility() di una vista con VISIBLE, INVISIBLE o GONE. Le visualizzazioni rimanenti nel gruppo di visualizzazioni possono animarsi nelle nuove posizioni anche 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 eseguita sugli elementi visualizzati nel contenitore.
  • CHANGE_APPEARING: un flag che indica l'animazione eseguita sugli elementi che cambiano in seguito alla visualizzazione di un nuovo elemento nel contenitore.
  • DISAPPEARING: un flag che indica l'animazione eseguita per gli elementi che scompaiono dal contenitore.
  • CHANGE_DISAPPEARING: un flag che indica l'animazione eseguita sugli elementi che cambiano in seguito alla scomparsa di un elemento dal contenitore.

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

Per impostare l'attributo android:animateLayoutchanges su true per il ViewGroup, segui questi passaggi:

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

Impostando questo attributo su true, anima automaticamente le viste aggiunte o rimosse dal gruppo di visualizzazioni, nonché le viste rimanenti al suo interno.

Animazione delle modifiche dello stato della visualizzazione utilizzando StateListAnimator

La classe StateListAnimator consente di definire gli animatori da eseguire quando lo stato di una visualizzazione 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 "attivo") cambia.

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

Ad esempio, il seguente file crea un animatore con elenco di stati che cambia la scala x e y della visualizzazione quando viene premuto:

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

In alternativa, invece di animare le proprietà della visualizzazione, puoi riprodurre un'animazione disegnabile tra le modifiche 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>

Utilizza un valutatore dei tipi

Se vuoi animare un tipo sconosciuto al sistema Android, puoi creare il tuo valutatore 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 per l'implementazione nell'interfaccia TypeEvaluator il metodo evaluate(). Ciò consente all'animatore che stai utilizzando di restituire un valore appropriato per la tua proprietà animata nel punto corrente dell'animazione. Il corso FloatEvaluator illustra 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 dell'animazione trascorsa (un valore compreso tra 0 e 1) e ne calcola una versione interpolata in base all'interpolatore in uso. La frazione interpolata è ciò che il tuo TypeEvaluator riceve tramite il parametro fraction, quindi non devi tenere conto dell'interpolatore durante il calcolo dei valori animati.

Utilizzare interpolatori

Un interpolatore definisce il modo in cui i valori specifici di un'animazione vengono calcolati come funzione del tempo. Ad esempio, puoi specificare le animazioni in modo che si verifichino in modo lineare per tutta la durata dell'animazione, ovvero in modo uniforme, oppure puoi specificare le animazioni in modo che utilizzino il 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 dagli animatori una frazione che rappresenta 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 nessuno soddisfa le tue esigenze, puoi implementare l'interfaccia TimeInterpolator e creare la tua.

Ad esempio, 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 decela al di fuori. I seguenti metodi definiscono la logica di questi interpolatori:

AcceleraDecelerataInterpolatore

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

InterpolatoreLineare

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 trascorsa/Frazione interpolata (lineare) Frazione interpolata (Accelera/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 modifica i valori alla stessa velocità, ovvero 0,2 per ogni 200 ms trasmessi. AccelerateDecelerateInterpolator modifica i valori più velocemente di LinearInterpolator tra 200 ms e 600 ms e più lentamente tra 600 ms e 1000 ms.

Specifica i fotogrammi chiave

Un oggetto Keyframe è costituito da una coppia di tempo/valore che 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 quella del fotogramma chiave precedente.

Per creare l'istanza di un oggetto Keyframe, devi utilizzare uno dei metodi di fabbrica, ofInt(), ofFloat() o ofObject() per ottenere il tipo appropriato di Keyframe. Quindi, chiami 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. Lo snippet di codice riportato di seguito 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);

Visualizzazioni animata

Il sistema di animazione delle proprietà consente un'animazione semplificata degli oggetti View e offre alcuni vantaggi rispetto al sistema di animazione delle viste. Il sistema di animazione visivo ha trasformato gli oggetti View cambiando il modo in cui erano disegnati. Questa operazione è stata gestita nel contenitore di ogni vista, perché quest'ultima non aveva proprietà da manipolare. Di conseguenza, la vista è stata animata, ma non ha apportato modifiche all'oggetto View. Ciò ha causato comportamenti quali un oggetto ancora esistente nella sua posizione originale, anche se era stato disegnato in un'altra posizione 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 Visualizza. Inoltre, le visualizzazioni richiamano automaticamente il metodo invalidate() per aggiornare la schermata ogni volta che ne vengono modificate le proprietà. Le nuove proprietà della classe View che facilitano le animazioni delle proprietà sono:

  • translationX e translationY: queste proprietà controllano la posizione della vista sotto forma di delta dalle coordinate in alto e a sinistra, che sono 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 la scalabilità 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à che descrivono la posizione finale della vista nel relativo container, come somma dei valori di sinistra e di primo e dei valori di traslazioneX e traduzioneY.
  • alpha: rappresenta la trasparenza alpha sulla vista. Questo valore è 1 (opaco) per impostazione predefinita, dove il valore 0 rappresenta la massima trasparenza (non visibile).

Per animare una proprietà di un oggetto Visualizza, come il colore o il valore di rotazione, devi creare un animatore della proprietà e specificare la proprietà Vista che vuoi 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.

Creare animazioni con ViewPropertyAnimator

L'ViewPropertyAnimator offre un modo semplice per animare 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 quando anima più 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 se 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();

One 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 Android for Developers.

Dichiara le animazioni in XML

Il sistema di animazione delle proprietà consente di dichiarare le animazioni delle proprietà in 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 per l'animazione delle proprietà, 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 individuare gli attributi che puoi utilizzare nella tua dichiarazione XML, consulta la pagina Risorse dell'animazione. L'esempio seguente riproduce i due insiemi di animazioni degli oggetti in sequenza, con il primo set nidificato che riproduce due animazioni di 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 aumentare le risorse XML del codice fino a un oggetto AnimatorSet, quindi impostare gli oggetti di destinazione per tutte le animazioni prima di avviare il set di animazioni. Per praticità, la chiamata a setTarget() imposta un singolo oggetto di destinazione per tutti gli elementi secondari di AnimatorSet. 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 AnimatorUpdateListener, ottenere il valore di animazione aggiornato e utilizzarlo in una proprietà di una delle viste, come mostrato 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 causano un lavoro di rendering aggiuntivo per ogni frame in cui viene eseguita l'animazione. Per questo motivo, l'uso di animazioni che consumano molte risorse può influire negativamente sulle prestazioni dell'app.

Il lavoro necessario per animare l'interfaccia utente viene aggiunto alla fase di animazione della pipeline di rendering. Per sapere se le animazioni influiscono sulle prestazioni dell'app, attiva il rendering GPU del profilo e monitora la fase dell'animazione. Per maggiori informazioni, consulta la Procedura dettagliata per il rendering della GPU dei profili.