Il framework di transizione di Android consente di animare tutti i tipi di movimento nell'interfaccia utente fornendo i layout iniziale e finale. Puoi selezionare il tipo di animazione che preferisci, ad esempio con dissolvenza in entrata o uscita oppure modificare le dimensioni delle visualizzazioni, e il framework di transizione determina la modalità di animazione dal layout iniziale 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 visualizzazioni.
- Animazioni integrate: utilizza animazioni predefinite per effetti comuni come dissolvenza in uscita o movimento.
- Supporto dei file di risorse: carica le gerarchie di visualizzazioni e le animazioni integrate dai file di risorse di layout.
- callback del ciclo di vita: ricevono callback che forniscono il controllo sull'animazione e sul processo di modifica della gerarchia.
Per un codice di esempio che si anima tra le modifiche al layout, consulta Transizione di base.
La procedura di base per animare due layout è la seguente:
- Crea un oggetto
Scene
per il layout iniziale e finale. Tuttavia, la scena del layout iniziale è spesso determinata automaticamente dal layout corrente. - Crea un oggetto
Transition
per definire il tipo di animazione che vuoi. - Richiama
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.
Crea una scena
Le scene memorizzano lo stato di una gerarchia delle viste, incluse tutte le relative viste e i relativi valori delle proprietà. Il framework delle transizioni consente di eseguire animazioni tra una scena iniziale e una finale.
Puoi creare scene da un file di risorse di layout o da un gruppo di visualizzazioni nel codice. Tuttavia, la scena iniziale della transizione è spesso determinata automaticamente dall'interfaccia utente corrente.
Una scena può anche definire le azioni che vengono eseguite quando cambi scena. Questa funzionalità è utile per ripulire le impostazioni di visualizzazione dopo la transizione 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 delle visualizzazioni nel file è per lo più statica.
La scena risultante rappresenta lo stato della gerarchia delle visualizzazioni al momento della creazione dell'istanza Scene
. Se cambi la gerarchia delle viste,
ricrea la scena. Il framework crea la scena dall'intera
gerarchia delle viste nel 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 la funzione Scene.getSceneForLayout()
con la radice della scena e l'ID risorsa del file di layout che contiene la gerarchia delle visualizzazioni della 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 principale della scena. Gli snippet dimostrano inoltre che puoi caricare più oggetti Scene
non correlati senza lasciare intendere 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
ConstraintLayout
per la prima scena con due campi di testo. - Un
ConstraintLayout
per la seconda scena con gli stessi due campi di testo in ordine diverso.
L'esempio è progettato in modo che tutta l'animazione si trovi all'interno del layout secondario del layout principale per l'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 FrameLayout
secondario 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 anche
in una scena, poiché il framework può caricare solo un intero file di layout in
una 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 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 una scena per ognuno di essi. In questo modo puoi passare tra le due configurazioni della UI. Per ottenere una scena, è necessario 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 di viste. Entrambe le scene utilizzano la radice scena definita dall'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
. Utilizza 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 il costruttore Scene(sceneRoot, viewHierarchy)
. Chiamare questo costruttore equivale a chiamare la funzione Scene.getSceneForLayout()
quando hai già gonfiato un file di layout.
Il seguente snippet di codice mostra come creare un'istanza Scene
dall'elemento principale della scena e dalla gerarchia di visualizzazione della scena nel
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 per la scena che il sistema esegue quando entri o esce da una scena. In molti casi non è necessario definire azioni personalizzate per le scene, poiché il framework anima automaticamente il passaggio da una scena all'altra.
Le azioni scena sono utili per gestire questi casi:
- Per animare le visualizzazioni che non appartengono alla stessa gerarchia. Puoi animare le visualizzazioni delle scene iniziali e finali utilizzando le azioni di uscita e di ingresso.
- Per animare le visualizzazioni che il framework di transizioni non può animare automaticamente,
come gli oggetti
ListView
. Per ulteriori informazioni, consulta la sezione sulle limitazioni.
Per fornire azioni personalizzate per la scena, definisci le azioni come oggetti Runnable
e passale alle funzioni Scene.setExitAction()
o Scene.setEnterAction()
. Il framework chiama la funzione setExitAction()
sulla scena
iniziale prima di eseguire l'animazione di transizione e la funzione setEnterAction()
sulla scena finale dopo l'esecuzione dell'animazione di transizione.
Applicare una transizione
Il framework di transizione rappresenta lo stile dell'animazione tra le scene con un
oggetto Transition
. Puoi creare un'istanza di Transition
utilizzando sottoclassi
integrate, come
AutoTransition
e
Fade
, oppure
definire la tua transizione.
Quindi, puoi eseguire
l'animazione tra le scene passando il termine Scene
e Transition
a
TransitionManager.go()
.
Il ciclo di vita della transizione è simile al ciclo di vita dell'attività e rappresenta gli stati di transizione monitorati dal framework tra l'inizio e il completamento di un'animazione. In stati importanti del ciclo di vita, il framework richiama funzioni di callback che puoi implementare per regolare l'interfaccia utente in diverse fasi della transizione.
Crea una transizione
La sezione precedente mostra come creare scene che rappresentano lo stato di
diverse gerarchie di visualizzazioni. Dopo aver definito la scena iniziale e quella finale da alternare, crea un oggetto Transition
che definisca un'animazione.
Il framework consente di specificare una transizione integrata in un file di risorse e di gonfiarla nel codice oppure di 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 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 tutte 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/> |
Tiene traccia delle variazioni di visibilità delle visualizzazioni target nelle scene di inizio e fine e sposta le visualizzazioni dentro e fuori dai bordi della scena. |
Fade |
<fade/> |
Le visualizzazioni di fade_in calano.fade_out visualizzazioni in dissolvenza.fade_in_out (predefinito) esegue un fade_out seguito da un
fade_in .
|
Slide |
<slide/> |
Tiene traccia delle variazioni di visibilità delle visualizzazioni target nelle scene di inizio e 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 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 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 aumentare il numero di un'istanza 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 semplici istanze di transizione integrate 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, 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, ad esempio un'azione utente. Prendiamo come esempio 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 al contempo una transizione che fa dissolvere il pulsante di ricerca e appare in dissolvenza nei risultati di ricerca.
Per apportare un cambio di scena durante l'applicazione di una transizione in risposta a un evento nella tua attività, chiama la funzione di classe TransitionManager.go()
con la scena finale e l'istanza di transizione da utilizzare per l'animazione, come mostrato nello snippet seguente:
Kotlin
TransitionManager.go(endingScene, fadeTransition)
Java
TransitionManager.go(endingScene, fadeTransition);
Il framework modifica la gerarchia delle visualizzazioni all'interno della radice della scena con la gerarchia delle visualizzazioni della scena finale durante l'esecuzione dell'animazione specificata dall'istanza di transizione. La scena iniziale è la scena finale dell'ultima transizione. Se non esiste una transizione precedente, la scena iniziale viene determinata automaticamente in base allo stato attuale dell'interfaccia utente.
Se non specifichi un'istanza di transizione, il gestore di transizione 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
.
Scegli viste target specifiche
Per impostazione predefinita, il framework applica le transizioni a tutte le visualizzazioni nella scena iniziale e finale. In alcuni casi, potresti voler applicare un'animazione solo a un sottoinsieme
di visualizzazioni di una scena. Il framework ti consente di selezionare viste
specifiche da animare. Ad esempio, il framework non supporta l'animazione delle modifiche agli
oggetti ListView
, quindi non provare ad animarle durante una transizione.
Ogni vista animata dalla transizione è detta target. Puoi selezionare solo le destinazioni che fanno parte della gerarchia delle visualizzazioni 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 solo le viste specificate all'elenco dei target, chiama la funzione addTarget()
. Per maggiori informazioni, consulta la documentazione di riferimento dell'API per la classe Transition
.
Specifica più transizioni
Per ottenere il massimo impatto da un'animazione, abbinala al tipo di cambiamenti 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 una chiara indicazione che alcune visualizzazioni non sono più disponibili. Se sposti le viste in punti diversi dello schermo, è meglio animare il movimento in modo che gli utenti notino la nuova posizione delle viste.
Non è necessario scegliere una sola animazione, poiché il framework delle transizioni consente di combinare gli effetti dell'animazione in un set di transizioni contenente un gruppo di transizioni integrate o personalizzate individuali.
Per definire un set di transizioni da una raccolta di transizioni in XML, crea un file di risorse nella directory res/transitions/
ed elenca le transizioni sotto l'elemento TransitionSet
. Ad esempio, il seguente snippet mostra come specificare un set di transizioni che abbia 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 aumentare il valore della transizione impostata in un oggetto TransitionSet
nel codice, chiama la funzione TransitionInflater.from()
nella tua attività. La classe TransitionSet
si estende dalla classe Transition
, quindi puoi utilizzarla con un gestore di transizione come qualsiasi altra istanza Transition
.
Applicare una transizione senza scene
Modificare le gerarchie delle visualizzazioni non è l'unico modo per modificare l'interfaccia utente. Per apportare modifiche, puoi anche aggiungere, modificare e rimuovere le viste 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 della ricerca e un'icona di ricerca. Per cambiare l'interfaccia utente in modo che mostrino i risultati, rimuovi il pulsante di ricerca
quando l'utente lo tocca richiamando la funzione
ViewGroup.removeView()
e aggiungi i risultati di ricerca richiamando 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 delle viste modificata nel codice.
Se apporti modifiche all'interno della gerarchia della vista corrente in questo modo, non è necessario creare una scena. Puoi invece creare e applicare una transizione tra due stati di una gerarchia di visualizzazione utilizzando una transizione ritardata. Questa funzionalità del framework di transizione inizia con lo stato della gerarchia delle visualizzazioni corrente, registra le modifiche apportate alle sue viste e applica una transizione che anima le modifiche quando il sistema ridisegna l'interfaccia utente.
Per creare una transizione ritardata all'interno di una gerarchia di visualizzazione singola, segui questi passaggi:
- Quando si verifica l'evento che attiva la transizione, chiama la funzione
TransitionManager.beginDelayedTransition()
, che fornisce la vista principale di tutte le viste da modificare e la transizione da utilizzare. Il framework archivia lo stato attuale delle viste secondarie e i relativi valori delle proprietà. - 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à.
- 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 delle 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 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 gli stati di transizione monitorati dal framework durante il periodo tra una chiamata alla funzione TransitionManager.go()
e il completamento dell'animazione. In stati importanti 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 il valore di una proprietà
della vista dalla gerarchia iniziale alla gerarchia della vista finale
durante un cambio di scena. Non puoi semplicemente copiare il valore dalla vista iniziale alla vista nella gerarchia della vista finale, perché questa gerarchia non risulta gonfia fino al completamento della transizione. Devi invece archiviare il valore in una variabile e poi copiarlo nella gerarchia finale della visualizzazione quando il framework ha terminato la transizione. Per ricevere una notifica al termine della transizione, implementa la funzione TransitionListener.onTransitionEnd()
nella tua attività.
Per maggiori informazioni, consulta il riferimento API per la classe TransitionListener
.
Limitazioni
Questa sezione elenca alcuni limiti noti del framework delle transizioni:
- Le animazioni applicate a una
SurfaceView
potrebbero non essere visualizzate correttamente. Le istanzeSurfaceView
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 applicati a una
TextureView
. - I corsi che estendono
AdapterView
, comeListView
, gestiscono le visualizzazioni secondarie in modo incompatibile con il framework delle transizioni. Se provi ad animare una visualizzazione basata suAdapterView
, il display del dispositivo potrebbe non rispondere più. - Se provi a ridimensionare una
TextView
con un'animazione, il testo viene visualizzato in una nuova posizione prima che l'oggetto venga ridimensionato completamente. Per evitare questo problema, non animare il ridimensionamento delle viste che contengono testo.