Animazione delle modifiche al layout mediante una transizione

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

La struttura di transizione di Android consente di animare tutti i tipi di movimento in la tua UI fornendo i layout iniziale e finale. Puoi selezionare il tipo di animazione che preferisci, ad esempio le visualizzazioni con dissolvenza o di cambiare le dimensioni delle visualizzazioni, e il framework di transizione determina come animare dal layout iniziale a quello finale.

Il framework di transizione include le seguenti funzionalità:

  • Animazioni a livello di gruppo: applicare effetti di animazione a tutte le visualizzazioni in una gerarchia di visualizzazioni.
  • Animazioni integrate: usa animazioni predefinite per effetti comuni come dissolvenza in uscita o movimento.
  • Supporto dei file di risorse: carica gerarchie e animazioni integrate dai file di risorse di layout.
  • Callback del ciclo di vita: Ricevere callback che forniscono il controllo sull'animazione e sulla gerarchia processo di modifica.

Per un esempio di codice che si anima tra le modifiche al layout, consulta BasicTransizione.

La procedura di base per animare due layout è la seguente:

  1. Crea un oggetto Scene per layout iniziale e finale. Tuttavia, la scena del layout iniziale è viene spesso determinato automaticamente dal layout corrente.
  2. Crea una Transition per definire il tipo di animazione desiderato.
  3. Chiama TransitionManager.go(), e il sistema esegue l'animazione per scambiare i layout.

Il diagramma nella figura 1 illustra la relazione tra i tuoi layout, le scene, la transizione e l'animazione finale.

Figura 1. Illustrazione di base di il modo in cui il framework di transizione crea un'animazione.

Crea una scena

Le scene memorizzano lo stato di una gerarchia di viste, incluse tutte le relative viste e i relativi i valori delle proprietà. Il framework delle transizioni può eseguire animazioni tra una e una scena finale.

Puoi creare scene da un layout di risorse o da un gruppo di viste nel codice. Tuttavia, la scena iniziale per la transizione viene spesso determinata automaticamente nell'interfaccia utente corrente.

Una scena può anche definire le azioni che vengono eseguite quando cambi scena. Questa funzione è utile per ripulire le impostazioni della vista per passare a una scena.

Crea una scena da una risorsa di layout

Puoi creare un'istanza Scene direttamente da una risorsa di layout . Utilizza questa tecnica quando la gerarchia delle visualizzazioni nel file è per lo più statica. La scena risultante rappresenta lo stato della gerarchia delle visualizzazioni nel momento in cui ha creato l'istanza Scene. Se modifichi la gerarchia di visualizzazione, ricreare la scena. Il framework crea la scena dall'intera vista nella gerarchia del file. Non puoi creare una scena da parte di un file di layout.

Per creare un'istanza Scene da un file di risorse di layout, recupera la radice della scena dal layout come ViewGroup Quindi chiama il metodo Scene.getSceneForLayout() con la radice della scena e l'ID risorsa del file di layout che contiene la gerarchia di visualizzazione della scena.

Definisci i layout per le scene

Gli snippet di codice nel resto di questa sezione mostrano come creare scene diverse con lo stesso elemento principale scena. Gli snippet dimostrano inoltre è possibile caricare più oggetti Scene non correlati senza lasciare intendere che sono correlate tra loro.

L'esempio è costituito dalle seguenti definizioni di layout:

  • Il layout principale di un'attività con un'etichetta di testo e un elemento secondario FrameLayout
  • Un ConstraintLayout per la prima scena con due campi di testo.
  • Un ConstraintLayout per la seconda scena con gli stessi due campi di testo in in un ordine diverso.

L'esempio è progettato in modo che tutta l'animazione si verifichi all'interno del file secondario layout del layout principale per l'attività. L'etichetta di testo nel layout principale rimangono statici.

Il layout principale dell'attività è definito come segue:

res/layout/activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/master_layout">
    <TextView
        android:id="@+id/title"
        ...
        android:text="Title"/>
    <FrameLayout
        android:id="@+id/scene_root">
        <include layout="@layout/a_scene" />
    </FrameLayout>
</LinearLayout>

Questa definizione di layout contiene un campo di testo e un FrameLayout secondario per nella radice della scena. Il layout della prima scena è incluso nel file di layout principale. In questo modo l'app può visualizzarlo nell'interfaccia utente iniziale e caricarlo in una scena, dato che il framework può caricare solo un intero file di layout scena.

Il layout per la prima scena è definito come segue:

res/layout/a_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    
</androidx.constraintlayout.widget.ConstraintLayout>

Il layout della seconda scena contiene gli stessi due campi di testo, con le gli stessi ID, posizionati in un ordine diverso. È definito come segue:

res/layout/another_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    
</androidx.constraintlayout.widget.ConstraintLayout>

Genera scene dai layout

Dopo aver creato le definizioni dei due layout dei vincoli, puoi ottenere un scena per ciascuno di essi. In questo modo puoi passare da un'interfaccia utente configurazioni. Per ottenere una scena, devi fare riferimento alla radice della scena e al layout dell'ID risorsa.

Il seguente snippet di codice mostra come ottenere un riferimento alla radice della scena crea due oggetti Scene dai file di layout:

Kotlin

val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this)

Java

Scene aScene;
Scene anotherScene;

// Create the scene root for the scenes in this app.
sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

// Create the scenes.
aScene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this);
anotherScene =
    Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this);

Nell'app ora sono presenti due oggetti Scene in base alla visualizzazione gerarchie. Entrambe le scene utilizzano la radice scena definita dal Elemento FrameLayout in res/layout/activity_main.xml.

Crea una scena nel codice

Puoi anche creare un'istanza Scene nel codice da un Oggetto ViewGroup. Utilizzare questa tecnica quando modifichi le gerarchie delle viste direttamente nel codice o quando le generi in modo dinamico.

Per creare una scena da una gerarchia di visualizzazioni nel codice, utilizza la classe Scene(sceneRoot, viewHierarchy) come costruttore. Chiamare questo costruttore equivale a chiamare il Scene.getSceneForLayout() quando hai già gonfiato un file di layout.

Il seguente snippet di codice mostra come creare un Scene dall'elemento principale della scena e la gerarchia di visualizzazione della scena in il tuo codice:

Kotlin

val sceneRoot = someLayoutElement as ViewGroup
val viewHierarchy = someOtherLayoutElement as ViewGroup
val scene: Scene = Scene(sceneRoot, viewHierarchy)

Java

Scene mScene;

// Obtain the scene root element.
sceneRoot = (ViewGroup) someLayoutElement;

// Obtain the view hierarchy to add as a child of
// the scene root when this scene is entered.
viewHierarchy = (ViewGroup) someOtherLayoutElement;

// Create a scene.
mScene = new Scene(sceneRoot, mViewHierarchy);

Crea azioni scena

Il framework consente di definire azioni personalizzate della scena che il sistema esegue l'ingresso o l'uscita da una scena. In molti casi, la definizione di azioni scena personalizzate è non necessario, poiché il framework anima il passaggio da una scena all'altra automaticamente.

Le azioni scena sono utili per gestire questi casi:

  • Per animare le visualizzazioni che non appartengono alla stessa gerarchia. Puoi animare le visualizzazioni le scene iniziale e finale usando le azioni di uscita e di ingresso.
  • Per animare le visualizzazioni che il framework di transizioni non può animare automaticamente, ad esempio oggetti ListView. Per ulteriori informazioni informazioni, consulta la sezione sulle limitazioni.

Per fornire azioni personalizzate per la scena, definisci le azioni come Runnable oggetti e passali al Scene.setExitAction() oppure Scene.setEnterAction() funzioni. Il framework chiama la funzione setExitAction() all'inizio scena prima di eseguire l'animazione di transizione e setEnterAction() sulla scena finale dopo aver eseguito l'animazione di transizione.

Applicare una transizione

Il framework di transizione rappresenta lo stile dell'animazione tra le scene con un Transition oggetto. Puoi creare un'istanza di un Transition utilizzando di grandi dimensioni, come AutoTransition e Fade oppure definisci la tua transizione. Poi puoi eseguire l'animazione tra le scene superando Scene e Transition a TransitionManager.go().

Il ciclo di vita della transizione è simile al ciclo di vita dell'attività e rappresenta la transizione stabilisce che il framework monitora l'inizio completamento di un'animazione. In stati importanti del ciclo di vita, il framework richiama di callback che puoi implementare per regolare l'interfaccia utente fasi diverse della transizione.

Crea una transizione

La sezione precedente mostra come creare scene che rappresentino lo stato diverse gerarchie di oggetti View. Dopo aver definito le scene iniziali e finali, Se vuoi passare da un oggetto all'altro, crea un oggetto Transition che definisca un'animazione. Il framework consente di specificare una transizione integrata in un file di risorse e gonfiarlo nel codice oppure creare un'istanza di una transizione integrata direttamente nel codice.

Tabella 1. Tipi di transizione integrati.

Classe Tagga Effetto
AutoTransition <autoTransition/> Transizione predefinita. Dissolvenza in uscita, spostamento, ridimensionamento e dissolvenza nelle visualizzazioni, in questo ordine.
ChangeBounds <changeBounds/> Sposta e ridimensiona le visualizzazioni.
ChangeClipBounds <changeClipBounds/> Acquisisce View.getClipBounds() prima e dopo la scena modifiche e le anima durante la transizione.
ChangeImageTransform <changeImageTransform/> Acquisisce la matrice di un ImageView prima e dopo la scena lo modifichi e lo anima durante la transizione.
ChangeScroll <changeScroll/> Acquisisce le proprietà di scorrimento dei target prima e dopo la scena la modifica e l'animazione delle modifiche.
ChangeTransform <changeTransform/> Acquisisce la portata e la rotazione delle visualizzazioni prima e dopo il cambio di scena e anima queste modifiche durante la transizione.
Explode <explode/> Tiene traccia delle modifiche alla visibilità delle visualizzazioni target all'inizio e alla fine e sposta le visualizzazioni dentro o fuori dai bordi della scena.
Fade <fade/> Le visualizzazioni di fade_in calano.
fade_out visualizzazioni in dissolvenza.
fade_in_out (valore predefinito) esegue un fade_out seguito da un fade_in.
Slide <slide/> Tiene traccia delle modifiche alla visibilità delle visualizzazioni target all'inizio e alla fine e sposta le visualizzazioni dentro o fuori da uno dei bordi della scena.

Crea un'istanza di transizione da un file di risorse

Questa tecnica consente di modificare la definizione della transizione senza cambiare il codice della tua attività. Questa tecnica è utile anche per separare definizioni della transizione dal codice dell'applicazione, come mostrato nella sezione informazioni su come specificare più transizioni.

Per specificare una transizione integrata in un file di risorse, segui questi passaggi:

  • Aggiungi la directory res/transition/ al progetto.
  • Crea un nuovo file di risorsa XML all'interno di questa directory.
  • Aggiungi un nodo XML per una delle transizioni integrate.

Ad esempio, il seguente file di risorse specifica la transizione Fade:

res/transition/fade_transition.xml

<fade xmlns:android="http://schemas.android.com/apk/res/android" />

Il seguente snippet di codice mostra come gonfiare un'istanza Transition all'interno la tua attività da un file di risorse:

Kotlin

var fadeTransition: Transition =
    TransitionInflater.from(this)
                      .inflateTransition(R.transition.fade_transition)

Java

Transition fadeTransition =
        TransitionInflater.from(this).
        inflateTransition(R.transition.fade_transition);

Crea un'istanza di transizione nel codice

Questa tecnica è utile per creare oggetti di transizione dinamicamente se modificare l'interfaccia utente nel codice e creare semplici transizioni integrate di Compute Engine con pochi parametri o nessun parametro.

Per creare un'istanza di una transizione integrata, richiama una delle istanze nelle sottoclassi della classe Transition. Ad esempio, Il seguente snippet di codice crea un'istanza della transizione Fade:

Kotlin

var fadeTransition: Transition = Fade()

Java

Transition fadeTransition = new Fade();

Applicare una transizione

In genere applichi una transizione per passare da una gerarchia di visualizzazione all'altra in risposta a un evento, come un'azione utente. Ad esempio, considera un'app di ricerca: Quando l'utente inserisce un termine di ricerca e tocca il pulsante di ricerca, l'app cambia a una scena che rappresenta il layout dei risultati durante l'applicazione di una transizione il pulsante di ricerca esce con dissolvenze e i risultati di ricerca.

Per cambiare scena durante l'applicazione di una transizione in risposta a un evento in la tua attività, chiama la funzione della classe TransitionManager.go() con la scena e l'istanza di transizione da utilizzare per l'animazione, come mostrato nell'immagine seguente snippet:

Kotlin

TransitionManager.go(endingScene, fadeTransition)

Java

TransitionManager.go(endingScene, fadeTransition);

Il framework cambia la gerarchia della visualizzazione all'interno della radice della scena con la vista gerarchia dalla scena finale durante l'esecuzione dell'animazione specificata di transizione. La scena iniziale è la fine dell'ultima una transizione. Se non ci sono transizioni precedenti, viene determinata la scena iniziale. automaticamente dallo stato corrente dell'interfaccia utente.

Se non specifichi un'istanza di transizione, il gestore di transizione può applicare transizione automatica in modo ragionevole nella maggior parte delle situazioni. Per ulteriori informazioni, consulta il riferimento API per TransitionManager .

Scegli viste target specifiche

Il framework applica le transizioni a tutte le visualizzazioni nella scena iniziale e finale per impostazione predefinita. In alcuni casi, potresti voler applicare un'animazione solo a un sottoinsieme di visualizzazioni in una scena. Il framework ti consente di selezionare viste specifiche che vuoi si anima. Ad esempio, il framework non supporta l'animazione di modifiche a ListView oggetti, quindi non provare ad animarli durante una transizione.

Ogni vista animata dalla transizione è detta target. Puoi solo seleziona i target che fanno parte della gerarchia di visualizzazione associata a una scena.

Per rimuovere una o più visualizzazioni dall'elenco dei target, richiama il metodo removeTarget() prima di iniziare la transizione. Per aggiungere solo le viste specificate ai di target, richiama il addTarget() personalizzata. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API per Transition.

Specifica più transizioni

Per ottenere il massimo impatto da un'animazione, abbinala al tipo di modifiche che tra una scena e l'altra. Ad esempio, se rimuovi alcune viste e aggiungi altre tra una scena e l'altra, un'animazione di dissolvenza in uscita o in entrata fornisce un'immagine indica che alcune visualizzazioni non sono più disponibili. Se sposti le viste su punti diversi dello schermo, è meglio animare il movimento in modo che gli utenti noteranno la nuova posizione delle viste.

Non è necessario scegliere una sola animazione, poiché il framework delle transizioni consente di combinare effetti di animazione in un set di transizioni che contiene un gruppo di singole transizioni integrate o personalizzate.

Per definire un set di transizioni da una raccolta di transizioni in XML, crea un risorsa nella directory res/transitions/ ed elenca le transizioni in l'elemento TransitionSet. Ad esempio, il seguente snippet mostra come specificare un set di transizioni che abbia lo stesso comportamento di AutoTransition classe:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
    <fade android:fadingMode="fade_in" />
</transitionSet>

Per gonfiare il set di transizioni in un Oggetto TransitionSet in il tuo codice, chiama TransitionInflater.from() nella tua attività. La classe TransitionSet si estende dal Transition corso, per poterlo utilizzare con un gestore della transizione come qualsiasi altro un'altra istanza Transition.

Applicare una transizione senza scene

Modificare le gerarchie delle visualizzazioni non è l'unico modo per modificare l'interfaccia utente. Tu Puoi apportare modifiche anche aggiungendo, modificando e rimuovendo le viste secondarie all'interno nella gerarchia corrente.

Ad esempio, puoi implementare un'interazione di ricerca con un unico layout. Iniziare con il layout che mostra un campo di immissione della ricerca e una ricerca . Per modificare l'interfaccia utente in modo che mostrino i risultati, rimuovi il pulsante di ricerca quando l'utente lo tocca chiamando il ViewGroup.removeView() e aggiungi i risultati di ricerca richiamando ViewGroup.addView() personalizzata.

Puoi utilizzare questo approccio se l'alternativa è avere due gerarchie che sono quasi identiche. Anziché creare e gestire due file di layout separati per una lieve differenza nell'interfaccia utente, è possibile avere un solo file di layout contenenti una gerarchia di viste modificata nel codice.

Se apporti modifiche all'interno della gerarchia di visualizzazione corrente in questo modo, non per creare una scena. Puoi invece creare e applicare una transizione tra due stati di una gerarchia di viste utilizzando una transizione ritardata. Questa funzionalità il framework di transizioni inizia con lo stato corrente della gerarchia di visualizzazione, le modifiche apportate alle sue visualizzazioni e applica una transizione che anima cambia quando il sistema ridisegna l'interfaccia utente.

Per creare una transizione ritardata all'interno di una gerarchia di visualizzazione singola, segui questi passaggi: passaggi:

  1. Quando si verifica l'evento che attiva la transizione, chiama il metodo TransitionManager.beginDelayedTransition() che fornisce la vista padre di tutte le viste da modificare e la transizione da utilizzare. Il framework archivia lo stato lo stato delle viste secondarie e i relativi valori delle proprietà.
  2. Apporta le modifiche alle viste secondarie in base al tuo caso d'uso. Il framework registra le modifiche apportate alle viste secondarie e alle relative proprietà.
  3. Quando il sistema ridisegna l'interfaccia utente in base alle modifiche apportate, il parametro anima le modifiche tra lo stato originale e il nuovo stato.

L'esempio seguente mostra come animare l'aggiunta di una visualizzazione di testo a una visualizzazione della gerarchia utilizzando una transizione ritardata. Il primo snippet mostra il layout file di definizione:

res/layout/activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <EditText
        android:id="@+id/inputText"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    ...
</androidx.constraintlayout.widget.ConstraintLayout>

Lo snippet successivo mostra il codice che anima l'aggiunta della visualizzazione di testo:

MainActivity

Kotlin

setContentView(R.layout.activity_main)
val labelText = TextView(this).apply {
    text = "Label"
    id = R.id.text
}
val rootView: ViewGroup = findViewById(R.id.mainLayout)
val mFade: Fade = Fade(Fade.IN)
TransitionManager.beginDelayedTransition(rootView, mFade)
rootView.addView(labelText)

Java

private TextView labelText;
private Fade mFade;
private ViewGroup rootView;
...
// Load the layout.
setContentView(R.layout.activity_main);
...
// Create a new TextView and set some View properties.
labelText = new TextView(this);
labelText.setText("Label");
labelText.setId(R.id.text);

// Get the root view and create a transition.
rootView = (ViewGroup) findViewById(R.id.mainLayout);
mFade = new Fade(Fade.IN);

// Start recording changes to the view hierarchy.
TransitionManager.beginDelayedTransition(rootView, mFade);

// Add the new TextView to the view hierarchy.
rootView.addView(labelText);

// When the system redraws the screen to show this update,
// the framework animates the addition as a fade in.

Definisci i callback del ciclo di vita della transizione

Il ciclo di vita della transizione è simile al ciclo di vita dell'attività. Rappresenta afferma che il framework monitora il periodo che intercorre tra una chiamata alla funzione TransitionManager.go() e il completamento l'animazione. In stati importanti del ciclo di vita, il framework richiama i callback definita dal TransitionListener a riga di comando.

I callback del ciclo di vita della transizione sono utili, ad esempio, per copiare una vista valore di una proprietà dalla gerarchia della vista iniziale a quella della vista finale durante un cambio di scena. Non puoi semplicemente copiare il valore dalla visualizzazione iniziale la vista nella gerarchia della vista finale, perché quest'ultima non è gonfiato fino al completamento della transizione. Devi invece memorizzare il valore in una variabile e quindi la copia nella gerarchia della vista finale quando il framework ha terminato la transizione. Per ricevere una notifica al termine della transizione, di implementare TransitionListener.onTransitionEnd() nella tua attività.

Per ulteriori informazioni, consulta la documentazione di riferimento dell'API per TransitionListener .

Limitazioni

Questa sezione elenca alcuni limiti noti del framework delle transizioni:

  • Animazioni applicate a un L'app SurfaceView potrebbe non essere visualizzata in modo corretto. SurfaceView istanze vengono aggiornate da un thread non UI, quindi gli aggiornamenti potrebbero non essere sincronizzati con le animazioni di altre visualizzazioni.
  • Alcuni tipi specifici di transizione potrebbero non produrre l'effetto di animazione desiderato quando applicato a una TextureView.
  • Classi che si estendono AdapterView, ad esempio ListView, gestire le visualizzazioni dei propri figli in modi incompatibili con il framework sulle transizioni. Se provi ad animare una visualizzazione basata su AdapterView, il display del dispositivo potrebbe non rispondere più.
  • Se provi a ridimensionare una TextView con un'animazione, il testo compare in una nuova posizione prima che l'oggetto sia completamente viene ridimensionato. Per evitare questo problema, non animare il ridimensionamento delle viste che contengono testo.