Il framework di transizione di Android ti consente di animare tutti i tipi di movimento nella tua UI fornendo i layout iniziale e finale. Puoi selezionare il tipo di animazione che preferisci, ad esempio dissolvenze in entrata o in uscita delle visualizzazioni o modifiche delle dimensioni delle visualizzazioni, e il framework di transizione determina come animare il layout iniziale fino a quello finale.
Il framework di transizione include le seguenti funzionalità:
- Animazioni a livello di gruppo:applica effetti di animazione a tutte le visualizzazioni in una gerarchia di oggetti View.
- Animazioni integrate: utilizza animazioni predefinite per effetti comuni come dissolvenza in uscita o movimento.
- Supporto dei file di risorse: carica le gerarchie delle visualizzazioni e le animazioni integrate dai file di risorse di layout.
- Callback del ciclo di vita:ricevi callback che forniscono il controllo sull'animazione e sul processo di modifica della gerarchia.
Per un esempio di codice campione che anima le transizioni tra le modifiche del layout, vedi BasicTransition.
La procedura di base per l'animazione tra due layout è la seguente:
- Crea un oggetto
Sceneper i layout di inizio e fine. Tuttavia, la scena del layout iniziale viene spesso determinata automaticamente dal layout corrente. - Crea un oggetto
Transitionper definire il tipo di animazione che vuoi. - Chiama
TransitionManager.go(), e il sistema esegue l'animazione per scambiare i layout.
Il diagramma nella figura 1 illustra la relazione tra i layout, le scene, la transizione e l'animazione finale.
Figura 1. Illustrazione di base di come il framework di transizione crea un'animazione.
Creare una scena
Le scene memorizzano lo stato di una gerarchia di visualizzazioni, incluse tutte le visualizzazioni e i relativi valori delle proprietà. Il framework delle transizioni può eseguire animazioni tra una scena iniziale e una finale.
Puoi creare le scene da un file di risorse di layout o da un gruppo di visualizzazioni nel codice. Tuttavia, la scena iniziale della transizione viene spesso determinata automaticamente dall'interfaccia utente corrente.
Una scena può anche definire le proprie azioni che vengono eseguite quando viene modificata. Questa funzionalità è utile per ripristinare le impostazioni di visualizzazione dopo il passaggio a una scena.
Crea una scena da una risorsa di layout
Puoi creare un'istanza Scene direttamente da un file di risorse di layout. Utilizza questa tecnica quando la gerarchia di oggetti View nel file è per lo più statica.
La scena risultante rappresenta lo stato della gerarchia di oggetti View al momento della creazione dell'istanza Scene. Se modifichi la gerarchia di oggetti View, ricrea la scena. Il framework crea la scena dall'intera gerarchia
delle visualizzazioni nel file. Non puoi creare una scena da una 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 la funzione
Scene.getSceneForLayout()
con la radice della scena e l'ID risorsa del file di layout che
contiene la gerarchia delle visualizzazioni per la scena.
Definisci i layout per le scene
Gli snippet di codice nel resto di questa sezione mostrano come creare due
scene diverse con lo stesso elemento root della scena. Gli snippet dimostrano anche
che puoi caricare più oggetti Scene non correlati senza implicare che
siano correlati 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
ConstraintLayoutper la prima scena con due campi di testo. - Un
ConstraintLayoutper la seconda scena con gli stessi due campi di testo in ordine diverso.
L'esempio è progettato in modo che tutta l'animazione si svolga all'interno del layout secondario del layout principale dell'attività. L'etichetta di testo nel layout principale rimane statica.
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 elemento secondario FrameLayout per la
radice della scena. Il layout della prima scena è incluso nel file di layout principale.
In questo modo, l'app può visualizzarlo come parte dell'interfaccia utente iniziale e caricarlo in una scena, poiché il framework può caricare solo un intero file di layout in una scena.
Il layout della 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" > <TextView android:id="@+id/text_view1" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="Text Line 1" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> <TextView android:id="@+id/text_view2" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="Text Line 2" app:layout_constraintTop_toBottomOf="@id/text_view1" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Il layout della seconda scena contiene gli stessi due campi di testo, con gli stessi ID, ma in un ordine diverso. È definita 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" > <TextView android:id="@+id/text_view2" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="Text Line 2" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> <TextView android:id="@+id/text_view1" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="Text Line 1" app:layout_constraintTop_toBottomOf="@id/text_view2" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
Generare scene dai layout
Dopo aver creato le definizioni per i due layout vincolati, puoi ottenere una scena per ciascuno. In questo modo puoi passare da una configurazione dell'interfaccia utente all'altra. Per ottenere una scena, devi avere un riferimento alla radice della scena e all'ID risorsa del layout.
Il seguente snippet di codice mostra come ottenere un riferimento alla radice della scena e
creare 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 basati sulle gerarchie
delle visualizzazioni. Entrambe le scene utilizzano la radice della scena definita dall'elemento
FrameLayout in res/layout/activity_main.xml.
Creare una scena nel codice
Puoi anche creare un'istanza Scene nel codice da un oggetto
ViewGroup. Utilizza questa tecnica quando modifichi le gerarchie delle visualizzazioni
direttamente nel codice o quando le generi in modo dinamico.
Per creare una scena da una gerarchia di oggetti View nel codice, utilizza il costruttore
Scene(sceneRoot, viewHierarchy). Chiamare questo costruttore equivale a chiamare la funzione
Scene.getSceneForLayout()
quando hai già aumentato le dimensioni di un file di layout.
Il seguente snippet di codice mostra come creare un'istanza Scene dall'elemento root della scena e dalla gerarchia di oggetti View per la scena nel 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);
Creare azioni per le scene
Il framework consente di definire azioni di scena personalizzate che il sistema esegue quando si entra o si esce da una scena. In molti casi, la definizione di azioni di scena personalizzate è superflua, poiché il framework anima automaticamente il cambiamento tra le scene.
Le azioni per le scene sono utili per gestire questi casi:
- Per animare le visualizzazioni che non si trovano nella stessa gerarchia. Puoi animare le visualizzazioni per le scene iniziali e finali utilizzando le azioni di uscita e ingresso della scena.
- Per animare le visualizzazioni che il framework delle transizioni non può animare automaticamente,
come gli oggetti
ListView. Per ulteriori informazioni, consulta la sezione relativa alle limitazioni.
Per fornire azioni personalizzate per le scene, definisci le azioni come oggetti
Runnable e passali alle funzioni
Scene.setExitAction()
o Scene.setEnterAction(). Il framework chiama la funzione setExitAction() nella scena iniziale prima di eseguire l'animazione di transizione e la funzione setEnterAction() nella scena finale dopo l'esecuzione dell'animazione di transizione.
Applicare una transizione
Il framework di transizione rappresenta lo stile di animazione tra le scene con un oggetto Transition. Puoi creare un'istanza di un Transition utilizzando sottoclassi integrate, come AutoTransition e Fade, oppure definire la tua transizione.
Quindi, puoi eseguire l'animazione tra le scene passando la fine Scene e Transition a TransitionManager.go().
Il ciclo di vita della transizione è simile a quello del ciclo di vita dell'attività e rappresenta gli stati di transizione che il framework monitora tra l'inizio e il completamento di un'animazione. In importanti stati del ciclo di vita, il framework richiama funzioni di callback che puoi implementare per modificare l'interfaccia utente in diverse fasi della transizione.
Creare una transizione
La sezione precedente mostra come creare scene che rappresentano lo stato di diverse gerarchie di visualizzazione. Dopo aver definito le scene iniziale e finale tra cui vuoi passare, crea un oggetto Transition che definisca un'animazione.
Il framework ti consente di specificare una transizione integrata in un file di risorse
e di gonfiarla nel codice o di creare un'istanza di una transizione integrata
direttamente nel codice.
Tabella 1. Tipi di transizione integrati.
| Classe | Tag | Effetto |
|---|---|---|
AutoTransition |
<autoTransition/> |
Transizione predefinita. Le visualizzazioni vengono sfumate, spostate e ridimensionate, quindi vengono visualizzate in dissolvenza in questo ordine. |
ChangeBounds |
<changeBounds/> |
Sposta e ridimensiona le visualizzazioni. |
ChangeClipBounds |
<changeClipBounds/> |
Acquisisce View.getClipBounds() prima e dopo il cambio di scena
e anima le modifiche durante la transizione. |
ChangeImageTransform |
<changeImageTransform/> |
Acquisisce la matrice di un ImageView prima e dopo il cambio di scena
e la anima durante la transizione. |
ChangeScroll |
<changeScroll/> |
Acquisisce le proprietà di scorrimento dei target prima e dopo il cambio di scena e anima le modifiche. |
ChangeTransform |
<changeTransform/> |
Acquisisce la scala e la rotazione delle visualizzazioni prima e dopo il cambio di scena e anima queste modifiche durante la transizione. |
Explode |
<explode/> |
Traccia le modifiche alla visibilità delle viste target nelle scene iniziale e finale e sposta le viste all'interno o all'esterno dai bordi della scena. |
Fade |
<fade/> |
fade_in sbiadisce nelle visualizzazioni.fade_out sfuma le visualizzazioni.fade_in_out (opzione predefinita) esegue un fade_out seguito da
un fade_in.
|
Slide |
<slide/> |
Traccia le modifiche alla visibilità delle viste target nelle scene iniziale e finale e sposta le viste all'interno o all'esterno di uno dei bordi della scena. |
Crea un'istanza di transizione da un file di risorse
Questa tecnica ti consente di modificare la definizione della transizione senza modificare il codice dell'attività. Questa tecnica è utile anche per separare le definizioni di transizione complesse dal codice dell'applicazione, come mostrato nella sezione relativa alla specifica di più transizioni.
Per specificare una transizione integrata in un file di risorse, segui questi passaggi:
- Aggiungi la directory
res/transition/al tuo progetto. - Crea un nuovo file di risorse 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 di Transition all'interno
della 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 dinamicamente oggetti di transizione se modifichi l'interfaccia utente nel codice e per creare istanze di transizione integrate semplici con pochi o nessun parametro.
Per creare un'istanza di una transizione integrata, richiama uno dei costruttori pubblici nelle sottoclassi della classe Transition. Ad esempio, lo snippet di codice seguente 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, ad esempio 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 passa a una scena che rappresenta il layout dei risultati applicando una transizione che attenua il pulsante di ricerca e mostra i risultati di ricerca.
Per eseguire un cambio di scena durante l'applicazione di una transizione in risposta a un evento nella tua attività, chiama la funzione della classe TransitionManager.go() con la scena finale e l'istanza di transizione da utilizzare per l'animazione, come mostrato nel seguente snippet:
Kotlin
TransitionManager.go(endingScene, fadeTransition)
Java
TransitionManager.go(endingScene, fadeTransition);
Il framework modifica la gerarchia di oggetti View all'interno della radice della scena con la gerarchia di oggetti View della scena finale durante l'esecuzione dell'animazione specificata dall'istanza di transizione. La scena iniziale è la scena finale dell'ultima transizione. Se non è presente una transizione precedente, la scena iniziale viene determinata automaticamente dallo stato attuale dell'interfaccia utente.
Se non specifichi un'istanza di transizione, il gestore delle transizioni può applicare una
transizione automatica che esegue un'operazione ragionevole nella maggior parte delle situazioni. Per
maggiori informazioni, consulta il riferimento API per la classe
TransitionManager.
Scegliere visualizzazioni di destinazione specifiche
Il framework applica le transizioni a tutte le visualizzazioni nelle scene 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 le visualizzazioni specifiche che vuoi
animare. Ad esempio, il framework non supporta l'animazione delle modifiche agli oggetti ListView, quindi non tentare di animarli durante una transizione.
Ogni visualizzazione animata dalla transizione è chiamata target. Puoi selezionare solo i target che fanno parte della gerarchia di oggetti View associata a una scena.
Per rimuovere una o più viste dall'elenco dei target, chiama il metodo
removeTarget() prima di iniziare la transizione. Per aggiungere all'elenco dei target solo le visualizzazioni che specifichi, chiama la funzione addTarget(). Per saperne di più, consulta il riferimento API per la classe
Transition.
Specificare più transizioni
Per ottenere il massimo impatto da un'animazione, abbinala al tipo di modifiche che si verificano tra le scene. Ad esempio, se rimuovi alcune visualizzazioni e ne aggiungi altre tra le scene, un'animazione di dissolvenza in uscita o in entrata fornisce un'indicazione evidente che alcune visualizzazioni non sono più disponibili. Se sposti le visualizzazioni in punti diversi dello schermo, è meglio animare il movimento in modo che gli utenti notino la nuova posizione delle visualizzazioni.
Non devi scegliere una sola animazione, perché il framework delle transizioni ti consente di combinare gli effetti di animazione in un insieme di transizioni che contiene un gruppo di transizioni personalizzate o integrate individuali.
Per definire un insieme di transizioni da una raccolta di transizioni in XML, crea un file di risorse nella directory res/transitions/ ed elenca le transizioni nell'elemento TransitionSet. Ad esempio, il seguente snippet mostra come
specificare un insieme di transizioni che ha lo stesso comportamento della classe AutoTransition:
<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 espandere il set di transizioni in un oggetto TransitionSet nel codice, chiama la funzione TransitionInflater.from() nell'attività. La classe TransitionSet estende la classe Transition, quindi puoi utilizzarla con un gestore delle transizioni come qualsiasi altra istanza Transition.
Applicare una transizione senza scene
La modifica delle gerarchie di visualizzazione non è l'unico modo per modificare l'interfaccia utente. Puoi anche apportare modifiche aggiungendo, modificando e rimuovendo le visualizzazioni secondarie all'interno della gerarchia corrente.
Ad esempio, puoi implementare un'interazione di ricerca con
un singolo layout. Inizia con il layout che mostra un campo di immissione di ricerca e un'icona di ricerca. Per modificare l'interfaccia utente in modo da mostrare i risultati, rimuovi il pulsante di ricerca
quando l'utente lo tocca chiamando la funzione
ViewGroup.removeView()
e aggiungi i risultati di ricerca chiamando la funzione
ViewGroup.addView().
Puoi utilizzare questo approccio se l'alternativa è avere due gerarchie quasi identiche. Anziché creare e gestire due file di layout separati per una piccola differenza nell'interfaccia utente, puoi avere un file di layout contenente una gerarchia di visualizzazione che modifichi nel codice.
Se apporti modifiche all'interno della gerarchia di oggetti View corrente in questo modo, non devi creare una scena. In alternativa, puoi creare e applicare una transizione tra due stati di una gerarchia di visualizzazione utilizzando una transizione ritardata. Questa funzionalità del framework delle transizioni inizia con lo stato attuale della gerarchia delle visualizzazioni, registra le modifiche apportate alle visualizzazioni e applica una transizione che anima le modifiche quando il sistema ridisegna l'interfaccia utente.
Per creare una transizione ritardata all'interno di una singola gerarchia di visualizzazione:
- Quando si verifica l'evento che attiva la transizione, chiama la funzione
TransitionManager.beginDelayedTransition(), fornendo la vista genitore di tutte le visualizzazioni che vuoi modificare e la transizione da utilizzare. Il framework memorizza lo stato attuale delle visualizzazioni secondarie e i relativi valori delle proprietà. - Apporta le modifiche alle visualizzazioni secondarie in base al tuo caso d'uso. Il framework registra le modifiche apportate alle viste secondarie e alle relative proprietà.
- Quando il sistema ridisegna l'interfaccia utente in base alle modifiche, il framework 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 gerarchia di visualizzazioni utilizzando una transizione ritardata. Il primo snippet mostra il file di definizione del layout:
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 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 gli stati di transizione che il framework monitora durante il periodo compreso tra una chiamata alla funzione TransitionManager.go() e il completamento dell'animazione. In importanti stati del ciclo di vita, il framework richiama i callback
definiti dall'interfaccia TransitionListener.
I callback del ciclo di vita della transizione sono utili, ad esempio, per copiare un valore della proprietà di visualizzazione dalla gerarchia di oggetti View iniziale a quella finale durante un cambio di scena. Non puoi semplicemente copiare il valore dalla visualizzazione iniziale alla visualizzazione nella gerarchia di oggetti View finale, perché la gerarchia di oggetti View finale non viene eseguita l'inflate finché la transizione non è completata. Devi invece memorizzare il valore
in una variabile e poi copiarlo nella gerarchia di visualizzazione finale quando il framework
ha completato la transizione. Per ricevere una notifica al termine della transizione,
implementa la funzione
TransitionListener.onTransitionEnd()
nella tua attività.
Per saperne di più, consulta il riferimento API per la classe
TransitionListener.
Limitazioni
Questa sezione elenca alcune limitazioni note del framework delle transizioni:
- Le animazioni applicate a un
SurfaceViewpotrebbero non essere visualizzate correttamente. Le istanzeSurfaceViewvengono aggiornate da un thread non UI, pertanto gli aggiornamenti potrebbero non essere sincronizzati con le animazioni di altre visualizzazioni. - Alcuni tipi di transizione specifici potrebbero non produrre l'effetto di animazione desiderato
se applicati a un
TextureView. - Le classi che estendono
AdapterView, ad esempioListView, gestiscono le visualizzazioni dei figli in modi incompatibili con il framework delle transizioni. Se provi ad animare una visualizzazione in base aAdapterView, il display del dispositivo potrebbe smettere di rispondere. - Se provi a ridimensionare un
TextViewcon un'animazione, il testo viene spostato in una nuova posizione prima che l'oggetto venga completamente ridimensionato. Per evitare questo problema, non animare il ridimensionamento delle visualizzazioni che contengono testo.