1. Prima di iniziare
Questo codelab fa parte del corso Advanced Android in Kotlin. Per ottenere il massimo valore da questo corso, ti consigliamo di seguire i codelab in sequenza, ma non è obbligatorio. Tutti i codelab del corso sono elencati nella pagina di destinazione dei codelab Advanced Android in Kotlin.
MotionLayout è una libreria che ti consente di aggiungere movimenti complessi alla tua app per Android. Si basa su ConstraintLayout, e ti consente di animare qualsiasi elemento che puoi creare utilizzando ConstraintLayout.
Puoi utilizzare MotionLayout per animare la posizione, le dimensioni, la visibilità, l'alfa, il colore, l'elevazione, la rotazione e altri attributi di più visualizzazioni contemporaneamente. Utilizzando XML dichiarativo, puoi creare animazioni coordinate, che coinvolgono più visualizzazioni, difficili da ottenere nel codice.
Le animazioni sono un ottimo modo per migliorare l'esperienza con un'app. Puoi utilizzare le animazioni per:
- Mostra modifiche: l'animazione tra gli stati consente all'utente di monitorare naturalmente le modifiche nell'interfaccia utente.
- Attirare l'attenzione: utilizza le animazioni per attirare l'attenzione sugli elementi importanti della UI.
- Crea progetti accattivanti: il movimento efficace nel design rende le app più raffinate.
Prerequisiti
Questo codelab è stato progettato per sviluppatori con una certa esperienza di sviluppo Android. Prima di tentare di completare questo codelab, devi:
- Scopri come creare un'app con un'attività, un layout di base ed eseguirla su un dispositivo o un emulatore utilizzando Android Studio. Familiarizza con
ConstraintLayout. Per saperne di più suConstraintLayout, leggi il codelab Constraint Layout.
Attività previste
- Definisci un'animazione con
ConstraintSetseMotionLayout - Animare in base agli eventi di trascinamento
- Modifica l'animazione con
KeyPosition - Modificare gli attributi con
KeyAttribute - Eseguire animazioni con il codice
- Animare le intestazioni comprimibili con
MotionLayout
Che cosa ti serve
- Android Studio 4.0 (l'editor
MotionLayoutfunziona solo con questa versione di Android Studio).
2. Per iniziare
Per scaricare l'app di esempio, puoi:
... o clona il repository GitHub dalla riga di comando utilizzando il seguente comando:
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. Creare animazioni con MotionLayout
Innanzitutto, creerai un'animazione che sposta una visualizzazione dall'inizio della parte superiore dello schermo alla fine della parte inferiore in risposta ai clic degli utenti.
Per creare un'animazione dal codice iniziale, ti serviranno i seguenti elementi principali:
- Un
MotionLayout,che è una sottoclasse diConstraintLayout. Specifichi tutte le visualizzazioni da animare all'interno del tagMotionLayout. - Un
MotionScene,, ovvero un file XML che descrive un'animazione perMotionLayout. - Un
Transition,che fa parte delMotionSceneche specifica la durata dell'animazione, il trigger e come spostare le visualizzazioni. - Un
ConstraintSetche specifica i vincoli di inizio e fine della transizione.
Esaminiamoli uno alla volta, a partire dal MotionLayout.
Passaggio 1: esplora il codice esistente
MotionLayout è una sottoclasse di ConstraintLayout, quindi supporta tutte le stesse funzionalità e aggiunge l'animazione. Per utilizzare MotionLayout, aggiungi una visualizzazione MotionLayout in cui utilizzeresti ConstraintLayout.
- In
res/layout, apriactivity_step1.xml.. Qui hai unConstraintLayoutcon una solaImageViewdi una stella, con una tinta applicata all'interno.
activity_step1.xml
<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/red_star"
...
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Questo ConstraintLayout non ha vincoli, quindi se esegui l'app ora vedrai la visualizzazione della stella senza vincoli, il che significa che sarà posizionata in una posizione sconosciuta. Android Studio ti avviserà della mancanza di vincoli.
Passaggio 2: converti in Motion Layout
Per animare utilizzando MotionLayout,, devi convertire ConstraintLayout in un MotionLayout.
Affinché il layout utilizzi una scena di movimento, deve puntare a quest'ultima.
- Per farlo, apri la superficie di progettazione. In Android Studio 4.0, apri la superficie di progettazione utilizzando l'icona di divisione o di progettazione in alto a destra quando visualizzi un file XML di layout.

- Una volta aperta la superficie di progettazione, fai clic con il tasto destro del mouse sull'anteprima e seleziona Converti in MotionLayout.

In questo modo, il tag ConstraintLayout viene sostituito da un tag MotionLayout e al tag MotionLayout viene aggiunto un motion:layoutDescription che punta a @xml/activity_step1_scene.
activity_step1**.xml**
<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/activity_step1_scene">
Una scena di movimento è un singolo file XML che descrive un'animazione in un MotionLayout.
Non appena esegui la conversione a un MotionLayout, nell'area di progettazione viene visualizzato l'Editor di movimento

Nell'editor di movimento sono presenti tre nuovi elementi dell'interfaccia utente:
- Panoramica: questa è una selezione modale che ti consente di selezionare diverse parti dell'animazione. In questa immagine è selezionato
startConstraintSet. Puoi anche selezionare la transizione trastarteendfacendo clic sulla freccia tra i due valori. - Sezione: sotto la panoramica si trova una finestra della sezione che cambia in base all'elemento della panoramica attualmente selezionato. In questa immagine, le informazioni
startConstraintSetvengono visualizzate nella finestra di selezione. - Attributo: il riquadro degli attributi mostra e consente di modificare gli attributi dell'elemento selezionato corrente dalla finestra di panoramica o di selezione. In questa immagine vengono mostrati gli attributi per
startConstraintSet.
Passaggio 3: definisci i vincoli di inizio e fine
Tutte le animazioni possono essere definite in termini di inizio e fine. L'inizio descrive l'aspetto dello schermo prima dell'animazione, mentre la fine descrive l'aspetto dello schermo dopo il completamento dell'animazione. MotionLayout è responsabile di capire come animare tra lo stato iniziale e quello finale (nel tempo).
MotionScene utilizza un tag ConstraintSet per definire gli stati iniziale e finale. Un ConstraintSet è un insieme di vincoli che possono essere applicati alle viste. Sono inclusi larghezza, altezza e vincoli ConstraintLayout. Include anche alcuni attributi come alpha. Non contiene le visualizzazioni in sé, ma solo i vincoli relativi a queste visualizzazioni.
I vincoli specificati in un ConstraintSet sostituiranno quelli specificati nel file di layout. Se definisci vincoli sia nel layout che in MotionScene, vengono applicati solo i vincoli in MotionScene.
In questo passaggio, vincola la visualizzazione a stella in modo che inizi nella parte superiore dello schermo e termini nella parte inferiore.
Puoi completare questo passaggio utilizzando l'editor di movimento o modificando direttamente il testo di activity_step1_scene.xml.
- Seleziona
startConstraintSet nel riquadro Panoramica.

- Nel riquadro Selezione, seleziona
red_star. Attualmente viene visualizzata l'origine dilayout, il che significa che non è vincolata in questoConstraintSet. Utilizza l'icona a forma di matita in alto a destra per creare un vincolo.

- Verifica che
red_starmostri un'origine distartquandostartConstraintSetè selezionato nel riquadro della panoramica. - Nel riquadro Attributi, con
red_starselezionato instartConstraintSet, aggiungi un vincolo in alto e inizia facendo clic sui pulsanti blu +.

- Apri
xml/activity_step1_scene.xmlper visualizzare il codice generato da Motion Editor per questo vincolo.
activity_step1_scene.xml
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
ConstraintSet ha un id di @id/start e specifica tutti i vincoli da applicare a tutte le visualizzazioni in MotionLayout. Poiché questo MotionLayout ha una sola vista, ha bisogno di un solo Constraint.
Constraint all'interno di ConstraintSet specifica l'ID della vista che sta vincolando, @id/red_star definito in activity_step1.xml. È importante notare che i tag Constraint specificano solo vincoli e informazioni sul layout. Il tag Constraint non sa di essere applicato a un ImageView.
Questo vincolo specifica l'altezza, la larghezza e gli altri due vincoli necessari per vincolare la visualizzazione red_star all'inizio della parte superiore del relativo elemento principale.
- Seleziona
endConstraintSet nel riquadro Panoramica.

- Segui gli stessi passaggi eseguiti in precedenza per aggiungere un
Constraintperred_starinendConstraintSet. - Per utilizzare Motion Editor per completare questo passaggio, aggiungi un vincolo a
bottomeendfacendo clic sui pulsanti + blu.

- Il codice in XML ha il seguente aspetto:
activitiy_step1_scene.xml
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
Come @id/start, questo ConstraintSet ha un solo Constraint su @id/red_star. Questa volta lo vincola alla parte inferiore dello schermo.
Non devi chiamarli @id/start e @id/end, ma è comodo farlo.
Passaggio 4: definisci una transizione
Ogni MotionScene deve includere anche almeno una transizione. Una transizione definisce ogni parte di un'animazione, dall'inizio alla fine.
Una transizione deve specificare un ConstraintSet di inizio e di fine. Una transizione può anche specificare come modificare l'animazione in altri modi, ad esempio per quanto tempo eseguirla o come animare trascinando le visualizzazioni.
- Motion Editor ha creato una transizione per noi per impostazione predefinita quando ha creato il file MotionScene. Apri
activity_step1_scene.xmlper visualizzare la transizione generata.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<KeyFrameSet>
</KeyFrameSet>
</Transition>
Questo è tutto ciò di cui MotionLayout ha bisogno per creare un'animazione. Esaminando ogni attributo:
constraintSetStartverrà applicato alle visualizzazioni all'avvio dell'animazione.constraintSetEndverrà applicato alle visualizzazioni al termine dell'animazione.durationspecifica la durata dell'animazione in millisecondi.
MotionLayout calcolerà quindi un percorso tra i vincoli di inizio e fine e lo animerà per la durata specificata.
Passaggio 5: visualizza l'anteprima dell'animazione nell'editor di movimento

Animazione:video della riproduzione di un'anteprima della transizione in Motion Editor
- Apri l'editor di movimento e seleziona la transizione facendo clic sulla freccia tra
starteendnel riquadro di panoramica.

- Il pannello Selezione mostra i controlli di riproduzione e una barra di scorrimento quando viene selezionata una transizione. Fai clic su Riproduci o trascina la posizione corrente per visualizzare l'anteprima dell'animazione.

Passaggio 6: aggiungi un gestore on click
Devi avere un modo per avviare l'animazione. Un modo per farlo è fare in modo che MotionLayout risponda agli eventi di clic su @id/red_star.
- Apri l'editor di movimento e seleziona la transizione facendo clic sulla freccia tra l'inizio e la fine nel riquadro di panoramica.

- Fai clic su
Crea gestore di clic o scorrimento nella barra degli strumenti del riquadro di panoramica . Viene aggiunto un handle che avvierà una transizione. - Seleziona Gestore clic dal popup.

- Modifica il valore Visualizzazioni per clic in
red_star.

- Fai clic su Aggiungi. Il gestore dei clic è rappresentato da un piccolo punto nella transizione nell'editor di movimento.

- Con la transizione selezionata nel riquadro Panoramica, aggiungi un attributo
clickActionditoggleal gestore OnClick che hai appena aggiunto nel riquadro degli attributi.

- Apri
activity_step1_scene.xmlper visualizzare il codice generato da Motion Editor.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
Transition indica a MotionLayout di eseguire l'animazione in risposta agli eventi di clic utilizzando un tag <OnClick>. Esaminando ogni attributo:
targetIdè la visualizzazione da monitorare per i clic.clickActionditogglepassa dallo stato iniziale a quello finale con un clic. Puoi visualizzare altre opzioni perclickActionnella documentazione.
- Esegui il codice, fai clic su Passaggio 1, quindi fai clic sulla stella rossa e guarda l'animazione.
Passaggio 5: le animazioni in azione
Esegui l'app. Quando fai clic sulla stella, dovresti vedere l'animazione.

Il file della scena di movimento completata definisce un Transition che punta a un ConstraintSet iniziale e finale.
All'inizio dell'animazione (@id/start), l'icona a forma di stella è vincolata alla parte superiore dello schermo. Alla fine dell'animazione (@id/end), l'icona a forma di stella è vincolata all'estremità inferiore dello schermo.
<?xml version="1.0" encoding="utf-8"?>
<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
</MotionScene>
4. Animazione basata su eventi di trascinamento
Per questo passaggio, creerai un'animazione che risponde a un evento di trascinamento dell'utente (quando l'utente scorre lo schermo) per eseguire l'animazione. MotionLayout supporta il monitoraggio degli eventi touch per spostare le visualizzazioni, nonché i gesti di scorrimento basati sulla fisica per rendere il movimento fluido.
Passaggio 1: esamina il codice iniziale
- Per iniziare, apri il file di layout
activity_step2.xml, che contiene unMotionLayoutesistente. Dai un'occhiata al codice.
activity_step2.xml
<!-- initial code -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/step2" >
<ImageView
android:id="@+id/left_star"
...
/>
<ImageView
android:id="@+id/right_star"
...
/>
<ImageView
android:id="@+id/red_star"
...
/>
<TextView
android:id="@+id/credits"
...
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Questo layout definisce tutte le visualizzazioni dell'animazione. Le tre icone a forma di stella non sono vincolate nel layout perché verranno animate nella scena di movimento.
I crediti TextView hanno dei vincoli applicati, perché rimangono nella stessa posizione per l'intera animazione e non modificano alcun attributo.
Passaggio 2: anima la scena
Come l'ultima animazione, l'animazione sarà definita da un ConstraintSet, iniziale e finale e da un Transition.
Definisci il ConstraintSet iniziale
- Apri la scena di movimento
xml/step2.xmlper definire l'animazione. - Aggiungi i vincoli per il vincolo iniziale
start. All'inizio, tutte e tre le stelle sono centrate nella parte inferiore dello schermo. Le stelle a destra e a sinistra hanno un valorealphapari a0.0, il che significa che sono completamente trasparenti e nascoste.
step2.xml
<!-- TODO apply starting constraints -->
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
In questo ConstraintSet, specifichi un Constraint per ogni stella. Ogni vincolo verrà applicato da MotionLayout all'inizio dell'animazione.
Ogni visualizzazione a stella è centrata nella parte inferiore dello schermo utilizzando i vincoli di inizio, fine e inferiore. Le due stelle @id/left_star e @id/right_star hanno entrambe un valore alfa aggiuntivo che le rende invisibili e che verrà applicato all'inizio dell'animazione.
I set di vincoli start e end definiscono l'inizio e la fine dell'animazione. Un vincolo all'inizio, come motion:layout_constraintStart_toStartOf, vincolerà l'inizio di una visualizzazione all'inizio di un'altra visualizzazione. All'inizio può essere un po' complicato perché il nome start viene utilizzato sia per che per e entrambi vengono utilizzati nel contesto dei vincoli. Per distinguere meglio i due concetti, start in layout_constraintStart si riferisce all'"inizio" della visualizzazione, ovvero a sinistra in una lingua con scrittura da sinistra a destra e a destra in una lingua con scrittura da destra a sinistra. Il set di vincoli start si riferisce all'inizio dell'animazione.
Definisci ConstraintSet finale
- Definisci il vincolo finale in modo da utilizzare una catena per posizionare tutte e tre le stelle insieme sotto
@id/credits. Inoltre, imposterà il valore finale dialphadelle stelle sinistra e destra su1.0.
step2.xml
<!-- TODO apply ending constraints -->
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintHorizontal_chainStyle="packed"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toStartOf="@id/red_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toEndOf="@id/left_star"
motion:layout_constraintEnd_toStartOf="@id/right_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintStart_toEndOf="@id/red_star"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>
Il risultato finale è che le visualizzazioni si diffondono verso l'alto e verso l'esterno dal centro durante l'animazione.
Inoltre, poiché la proprietà alpha è impostata su @id/right_start e @id/left_star in entrambi i ConstraintSets, entrambe le visualizzazioni appariranno in dissolvenza man mano che l'animazione procede.
Animazione basata sullo scorrimento dell'utente
MotionLayout può monitorare gli eventi di trascinamento dell'utente o uno scorrimento per creare un'animazione di "lancio" basata sulla fisica. Ciò significa che le visualizzazioni continueranno se l'utente le lancia e rallenteranno come farebbe un oggetto fisico quando rotola su una superficie. Puoi aggiungere questo tipo di animazione con un tag OnSwipe nel Transition.
- Sostituisci il TODO per l'aggiunta di un tag
OnSwipecon<OnSwipe motion:touchAnchorId="@id/red_star" />.
step2.xml
<!-- TODO add OnSwipe tag -->
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end">
<!-- MotionLayout will track swipes relative to this view -->
<OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>
OnSwipe contiene alcuni attributi, il più importante è touchAnchorId.
touchAnchorIdè la visualizzazione tracciata che si sposta in risposta al tocco.MotionLayoutmanterrà questa visualizzazione alla stessa distanza dal dito che scorre.touchAnchorSidedetermina quale lato della visualizzazione deve essere monitorato. Questo è importante per le visualizzazioni che vengono ridimensionate, seguono percorsi complessi o hanno un lato che si muove più velocemente dell'altro.dragDirectiondetermina la direzione importante per questa animazione (in alto, in basso, a sinistra o a destra).
Quando MotionLayout è in attesa di eventi di trascinamento, il listener viene registrato nella visualizzazione MotionLayout e non in quella specificata da touchAnchorId. Quando un utente inizia un gesto in un punto qualsiasi dello schermo, MotionLayout mantiene costante la distanza tra il dito e il touchAnchorSide della visualizzazione touchAnchorId. Se tocca a 100 dp dal lato dell'ancora, ad esempio, MotionLayout manterrà quel lato a 100 dp di distanza dal dito per l'intera animazione.
Prova
- Esegui di nuovo l'app e apri la schermata del passaggio 2. Vedrai l'animazione.
- Prova a "lanciare" o rilasciare il dito a metà dell'animazione per scoprire come
MotionLayoutmostra animazioni basate sulla fisica dei fluidi.

MotionLayout può animare tra design molto diversi utilizzando le funzionalità di ConstraintLayout per creare effetti avanzati.
In questa animazione, tutte e tre le visualizzazioni sono posizionate rispetto al relativo elemento principale nella parte inferiore dello schermo all'inizio. Alla fine, le tre visualizzazioni sono posizionate in relazione a @id/credits in una catena.
Nonostante questi layout molto diversi, MotionLayout creerà un'animazione fluida tra l'inizio e la fine.
5. Modifica di un percorso
In questo passaggio creerai un'animazione che segue un percorso complesso durante l'animazione e anima i crediti durante il movimento. MotionLayout può modificare il percorso che una visualizzazione seguirà tra l'inizio e la fine utilizzando un KeyPosition.
Passaggio 1: esplora il codice esistente
- Apri
layout/activity_step3.xmlexml/step3.xmlper visualizzare il layout e la scena di movimento esistenti. UnImageViewe unTextViewmostrano la luna e il testo dei crediti. - Apri il file della scena in movimento (
xml/step3.xml). Vedrai che è definita unaTransitionda@id/starta@id/end. L'animazione sposta l'immagine della luna dalla parte in basso a sinistra dello schermo a quella in basso a destra dello schermo utilizzando dueConstraintSets. Il testo dei crediti appare gradualmente daalpha="0.0"aalpha="1.0"mentre la luna si muove. - Esegui l'app ora e seleziona Passaggio 3. Se fai clic sulla luna, vedrai che segue un percorso lineare (o una linea retta) dall'inizio alla fine.
Passaggio 2: attiva il debug del percorso
Prima di aggiungere un arco al movimento della luna, è utile attivare il debug del percorso in MotionLayout.
Per sviluppare animazioni complesse con MotionLayout, puoi disegnare il percorso di animazione di ogni visualizzazione. Questa opzione è utile quando vuoi visualizzare l'animazione e perfezionare i piccoli dettagli del movimento.
- Per attivare i percorsi di debug, apri
layout/activity_step3.xmle aggiungimotion:motionDebug="SHOW_PATH"al tagMotionLayout.
activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
Dopo aver attivato il debug del percorso, quando esegui di nuovo l'app, vedrai i percorsi di tutte le visualizzazioni visualizzati con una linea tratteggiata.

- I cerchi rappresentano la posizione iniziale o finale di una visualizzazione.
- Le linee rappresentano il percorso di una visualizzazione.
- I rombi rappresentano un
KeyPositionche modifica il percorso.
Ad esempio, in questa animazione, il cerchio centrale è la posizione del testo dei crediti.
Passaggio 3: modifica un percorso
Tutte le animazioni in MotionLayout sono definite da un inizio e una fine ConstraintSet che definiscono l'aspetto dello schermo prima dell'inizio dell'animazione e al termine dell'animazione. Per impostazione predefinita, MotionLayout traccia un percorso lineare (una linea retta) tra la posizione iniziale e finale di ogni visualizzazione che cambia posizione.
Per creare percorsi complessi come l'arco della luna in questo esempio, MotionLayout utilizza una KeyPosition per modificare il percorso che una visualizzazione segue tra l'inizio e la fine.
- Apri
xml/step3.xmle aggiungi unKeyPositionalla scena. Il tagKeyPositionè posizionato all'interno del tagTransition.

step3.xml
<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Un KeyFrameSet è un elemento secondario di un Transition ed è un insieme di tutti i KeyFrames, ad esempio KeyPosition, che devono essere applicati durante la transizione.
Poiché MotionLayout calcola il percorso della luna tra l'inizio e la fine, lo modifica in base a KeyPosition specificato in KeyFrameSet. Puoi vedere come viene modificato il percorso eseguendo di nuovo l'app.
Un KeyPosition ha diversi attributi che descrivono come modifica il percorso. I più importanti sono:
framePositionè un numero compreso tra 0 e 100. Definisce il momento dell'animazione in cui deve essere applicato questoKeyPosition, dove 1 corrisponde all'1% dell'animazione e 99 al 99%. Quindi, se il valore è 50, lo applichi esattamente al centro.motionTargetè la visualizzazione per cui questoKeyPositionmodifica il percorso.keyPositionTypeè il modo in cui questoKeyPositionmodifica il percorso. Può essereparentRelative,pathRelativeodeltaRelative(come spiegato nel passaggio successivo).percentX | percentYindica di quanto modificare il percorso inframePosition(valori compresi tra 0,0 e 1,0, con valori negativi e valori >1 consentiti).
Puoi pensarlo in questo modo: "Alle ore framePosition modifica il percorso di motionTarget spostandolo di percentX o percentY in base alle coordinate determinate da keyPositionType".
Per impostazione predefinita, MotionLayout arrotonda gli angoli introdotti modificando il percorso. Se osservi l'animazione che hai appena creato, puoi notare che la luna segue un percorso curvo nella curva. Per la maggior parte delle animazioni, questo è il risultato che vuoi ottenere. In caso contrario, puoi specificare l'attributo curveFit per personalizzarlo.
Prova
Se esegui di nuovo l'app, vedrai l'animazione per questo passaggio.

La luna segue un arco perché passa attraverso un KeyPosition specificato in Transition.
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
Puoi leggere KeyPosition come: "A framePosition 50 (a metà dell'animazione) modifica il percorso di motionTarget @id/moon spostandolo di 50% Y (a metà schermo) in base alle coordinate determinate da parentRelative (l'intero MotionLayout)."
Quindi, a metà dell'animazione, la luna deve passare attraverso un KeyPosition che si trova al 50% dello schermo. Questo KeyPosition non modifica affatto il movimento X, quindi la luna continuerà ad andare dall'inizio alla fine in orizzontale. MotionLayout troverà un percorso fluido che passa per questo KeyPosition mentre si sposta tra l'inizio e la fine.
Se guardi da vicino, il testo dei crediti è vincolato dalla posizione della luna. Perché non si muove anche verticalmente?

<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
Si scopre che, anche se modifichi il percorso della luna, le posizioni iniziale e finale della luna non la spostano verticalmente. KeyPosition non modifica la posizione iniziale o finale, quindi il testo dei crediti è vincolato alla posizione finale della luna.
Se vuoi che i crediti si spostino con la luna, puoi aggiungere un KeyPosition ai crediti o modificare i vincoli di inizio su @id/credits.
Nella prossima sezione esamineremo i diversi tipi di keyPositionType in MotionLayout.
6. Informazioni su keyPositionType
Nell'ultimo passaggio hai utilizzato un tipo keyPosition di parentRelative per compensare il percorso del 50% dello schermo. L'attributo keyPositionType determina in che modo MotionLayout modificherà il percorso in base a percentX o percentY.
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Esistono tre diversi tipi di keyPosition possibili: parentRelative, pathRelative e deltaRelative. Se specifichi un tipo, il sistema di coordinate in base al quale vengono calcolati percentX e percentY cambierà.
Che cos'è un sistema di coordinate?
Un sistema di coordinate consente di specificare un punto nello spazio. Sono utili anche per descrivere una posizione sullo schermo.
MotionLayout sono un sistema di coordinate cartesiane. Ciò significa che hanno un asse X e un asse Y definiti da due rette perpendicolari. La differenza principale tra i due è la posizione dell'asse X sullo schermo (l'asse Y è sempre perpendicolare all'asse X).
Tutti i sistemi di coordinate in MotionLayout utilizzano valori compresi tra 0.0 e 1.0 sia sull'asse X che sull'asse Y. Consentono valori negativi e valori superiori a 1.0. Ad esempio, un valore percentX pari a -2.0 significa che devi andare nella direzione opposta dell'asse X due volte.
Se tutto questo ti sembra un po' troppo simile a una lezione di algebra, dai un'occhiata alle immagini qui sotto.
parentRelative coordinates

Il keyPositionType di parentRelative utilizza lo stesso sistema di coordinate dello schermo. Definisce (0, 0) in alto a sinistra dell'intero MotionLayout e (1, 1) in basso a destra.
Puoi utilizzare parentRelative ogni volta che vuoi creare un'animazione che si muove attraverso l'intero MotionLayout, come l'arco della luna in questo esempio.
Tuttavia, se vuoi modificare un percorso rispetto al movimento, ad esempio per farlo curvare leggermente, gli altri due sistemi di coordinate sono una scelta migliore.
deltaRelative coordinates

Delta è un termine matematico che indica la variazione, quindi deltaRelative è un modo per dire "variazione relativa". Nelle deltaRelativecoordinate(0,0) è la posizione iniziale della visualizzazione, mentre (1,1) è la posizione finale. Gli assi X e Y sono allineati allo schermo.
L'asse X è sempre orizzontale sullo schermo, mentre l'asse Y è sempre verticale. Rispetto a parentRelative, la differenza principale è che le coordinate descrivono solo la parte dello schermo in cui si sposterà la visualizzazione.
deltaRelative è un ottimo sistema di coordinate per controllare il movimento orizzontale o verticale in modo isolato. Ad esempio, puoi creare un'animazione che completa il movimento verticale (Y) al 50% e continua l'animazione orizzontalmente (X).
pathRelative coordinates

L'ultimo sistema di coordinate in MotionLayout è pathRelative. È molto diverso dagli altri due, in quanto l'asse X segue il percorso di animazione dall'inizio alla fine. Quindi (0,0) è la posizione iniziale e (1,0) è la posizione finale.
Perché vorresti farlo? A prima vista, la cosa è piuttosto sorprendente, soprattutto perché questo sistema di coordinate non è nemmeno allineato al sistema di coordinate dello schermo.
Si è scoperto che pathRelative è davvero utile per alcune cose.
- Accelerare, rallentare o interrompere una visualizzazione durante una parte dell'animazione. Poiché la dimensione X corrisponderà sempre esattamente al percorso seguito dalla visualizzazione, puoi utilizzare un
pathRelativeKeyPositionper modificare ilframePositionin cui viene raggiunto un punto specifico del percorso. Quindi, unKeyPositionaframePosition="50"con unpercentX="0.1"farà sì che l'animazione impieghi il 50% del tempo per percorrere il primo 10% del movimento. - Aggiungere un arco sottile a un percorso. Poiché la dimensione Y è sempre perpendicolare al movimento, la modifica di Y cambierà la curva del percorso rispetto al movimento complessivo.
- L'aggiunta di una seconda dimensione quando
deltaRelativenon funzionerà. Per il movimento completamente orizzontale e verticale,deltaRelativecreerà una sola dimensione utile. Tuttavia,pathRelativecreerà sempre coordinate X e Y utilizzabili.
Nel passaggio successivo imparerai a creare percorsi ancora più complessi utilizzando più di un KeyPosition.
7. Creare percorsi complessi
L'animazione che hai creato nell'ultimo passaggio genera una curva uniforme, ma la forma potrebbe essere più simile a una "luna".
Modificare un percorso con più elementi KeyPosition
MotionLayout può modificare ulteriormente un percorso definendo tutti i KeyPosition necessari per ottenere qualsiasi movimento. Per questa animazione creerai un arco, ma se vuoi puoi far saltare la luna su e giù al centro dello schermo.
- Apri
xml/step4.xml. Vedi che ha le stesse visualizzazioni e lo stessoKeyFrameche hai aggiunto nell'ultimo passaggio. - Per arrotondare la parte superiore della curva, aggiungi altri due
KeyPositionsal percorso di@id/moon, uno appena prima che raggiunga la parte superiore e uno dopo.

step4.xml
<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
Questi KeyPositions verranno applicati al 25% e al 75% dell'animazione e faranno spostare @id/moon lungo un percorso che si trova al 60% della parte superiore dello schermo. In combinazione con il KeyPosition esistente al 50%, si crea un arco uniforme che la luna può seguire.
In MotionLayout, puoi aggiungere tutti i KeyPositions che ti servono per ottenere il percorso di animazione che desideri. MotionLayout applicherà ogni KeyPosition al framePosition specificato e determinerà come creare un movimento fluido che attraversi tutti i KeyPositions.
Prova
- Esegui di nuovo l'app. Vai al Passaggio 4 per vedere l'animazione in azione. Quando fai clic sulla luna, questa segue il percorso dall'inizio alla fine, passando per ogni
KeyPositionspecificato inKeyFrameSet.
Esplora in autonomia
Prima di passare ad altri tipi di KeyFrame, prova ad aggiungere altri KeyPositions a KeyFrameSet per vedere che tipo di effetti puoi creare solo utilizzando KeyPosition.
Ecco un esempio che mostra come creare un percorso complesso che si sposta avanti e indietro durante l'animazione.

step4.xml
<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
motion:percentX="0.3"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
</KeyFrameSet>
Dopo aver esplorato KeyPosition, nel passaggio successivo passerai ad altri tipi di KeyFrames.
8. Modifica degli attributi durante il movimento
La creazione di animazioni dinamiche spesso comporta la modifica di size, rotation o alpha delle visualizzazioni man mano che l'animazione procede. MotionLayout supporta l'animazione di molti attributi in qualsiasi visualizzazione utilizzando un KeyAttribute.
In questo passaggio utilizzerai KeyAttribute per scalare e ruotare la luna. Utilizzerai anche un KeyAttribute per ritardare la comparsa del testo fino a quando la luna non avrà quasi completato il suo viaggio.
Passaggio 1: ridimensiona e ruota con KeyAttribute
- Apri
xml/step5.xmlche contiene la stessa animazione che hai creato nel passaggio precedente. Per variare, questa schermata utilizza un'immagine dello spazio diversa come sfondo. - Per far aumentare di dimensioni e ruotare la luna, aggiungi due tag
KeyAttributeinKeyFrameSetin corrispondenza dikeyFrame="50"ekeyFrame="100"

step5.xml
<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon"
android:scaleY="2.0"
android:scaleX="2.0"
android:rotation="-360"
/>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon"
android:rotation="-720"
/>
Questi KeyAttributes vengono applicati al 50% e al 100% dell'animazione. Il primo KeyAttribute al 50% si verifica nella parte superiore dell'arco e fa raddoppiare le dimensioni della visualizzazione, oltre a ruotarla di -360 gradi (o un cerchio completo). Il secondo KeyAttribute completerà la seconda rotazione a -720 gradi (due cerchi completi) e ridurrà le dimensioni a quelle normali, poiché i valori di scaleX e scaleY sono impostati su 1.0 per impostazione predefinita.
Proprio come un KeyPosition, un KeyAttribute utilizza framePosition e motionTarget per specificare quando applicare KeyFrame e quale vista modificare. MotionLayout eseguirà l'interpolazione tra KeyPositions per creare animazioni fluide.
KeyAttributes supporta gli attributi che possono essere applicati a tutte le visualizzazioni. Supportano la modifica di attributi di base come visibility, alpha o elevation. Puoi anche modificare la rotazione come stai facendo qui, ruotare in tre dimensioni con rotateX e rotateY, scalare le dimensioni con scaleX e scaleY o traslare la posizione della visualizzazione in X, Y o Z.
Passaggio 2: ritarda la visualizzazione dei crediti
Uno degli obiettivi di questo passaggio è aggiornare l'animazione in modo che il testo dei crediti non venga visualizzato finché l'animazione non è quasi completa.
- Per ritardare la visualizzazione dei crediti, definisci un altro
KeyAttributeche assicuri chealpharimanga 0 fino akeyPosition="85".MotionLayoutpasserà comunque senza problemi da 0 a 100 alpha, ma lo farà nell'ultimo 15% dell'animazione.
step5.xml
<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->
<KeyAttribute
motion:framePosition="85"
motion:motionTarget="@id/credits"
android:alpha="0.0"
/>
Questo KeyAttribute mantiene il alpha di @id/credits a 0,0 per il primo 85% dell'animazione. Poiché inizia con un valore alfa pari a 0, ciò significa che sarà invisibile per il primo 85% dell'animazione.
L'effetto finale di questo KeyAttribute è che i crediti vengono visualizzati verso la fine dell'animazione. In questo modo, sembra che siano coordinati con la Luna che si posiziona nell'angolo destro dello schermo.
Ritardando le animazioni in una visualizzazione mentre un'altra si sposta in questo modo, puoi creare animazioni impressionanti che risultano dinamiche per l'utente.
Prova
- Esegui di nuovo l'app e vai al passaggio 5 per vedere l'animazione in azione. Quando fai clic sulla luna, questa seguirà il percorso dall'inizio alla fine, passando per ogni
KeyAttributespecificato nelKeyFrameSet.

Poiché ruoti la luna di due cerchi completi, ora farà una doppia capriola all'indietro e i titoli di coda ritarderanno la loro comparsa fino a quando l'animazione non sarà quasi terminata.
Esplora in autonomia
Prima di passare al tipo finale di KeyFrame, prova a modificare altri attributi standard in KeyAttributes. Ad esempio, prova a cambiare rotation in rotationX per vedere quale animazione produce.
Ecco un elenco degli attributi standard che puoi provare:
android:visibilityandroid:alphaandroid:elevationandroid:rotationandroid:rotationXandroid:rotationYandroid:scaleXandroid:scaleYandroid:translationXandroid:translationYandroid:translationZ
9. Modifica degli attributi personalizzati
Le animazioni avanzate comportano la modifica del colore o di altri attributi di una visualizzazione. Mentre MotionLayout può utilizzare un KeyAttribute per modificare uno qualsiasi degli attributi standard elencati nell'attività precedente, utilizzi un CustomAttribute per specificare qualsiasi altro attributo.
Un CustomAttribute può essere utilizzato per impostare qualsiasi valore che abbia un setter. Ad esempio, puoi impostare backgroundColor su una visualizzazione utilizzando un CustomAttribute. MotionLayout utilizzerà la reflection per trovare il setter, quindi lo chiamerà ripetutamente per animare la visualizzazione.
In questo passaggio, utilizzerai un CustomAttribute per impostare l'attributo colorFilter sulla luna per creare l'animazione mostrata di seguito.

Definire gli attributi personalizzati
- Per iniziare, apri
xml/step6.xml, che contiene la stessa animazione che hai creato nel passaggio precedente. - Per far cambiare colore alla luna, aggiungi due
KeyAttributecon unCustomAttributeinKeyFrameSetakeyFrame="0",keyFrame="50"ekeyFrame="100".

step6.xml
<!-- TODO: Add Custom attributes here -->
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFB612"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
Aggiungi un CustomAttribute all'interno di un KeyAttribute. Il CustomAttribute verrà applicato al framePosition specificato da KeyAttribute.
All'interno di CustomAttribute devi specificare un attributeName e un valore da impostare.
motion:attributeNameè il nome del setter che verrà chiamato da questo attributo personalizzato. In questo esempio, verrà chiamatosetColorFilteril giornoDrawable.motion:custom*Valueè un valore personalizzato del tipo indicato nel nome. In questo esempio, il valore personalizzato è un colore specificato.
I valori personalizzati possono essere di uno dei seguenti tipi:
- Colore
- Numero intero
- Float
- Stringa
- Dimensione
- Booleano
Utilizzando questa API, MotionLayout può animare qualsiasi elemento che fornisca un setter su qualsiasi visualizzazione.
Prova
- Esegui di nuovo l'app e vai al passaggio 6 per vedere l'animazione in azione. Quando fai clic sulla luna, questa seguirà il percorso dall'inizio alla fine, passando per ogni
KeyAttributespecificato nelKeyFrameSet.

Quando aggiungi altri KeyFrames, MotionLayout cambia la traiettoria della luna da una linea retta a una curva complessa, aggiungendo una capriola all'indietro, un ridimensionamento e un cambio di colore a metà dell'animazione.
Nelle animazioni reali, spesso vengono animate più viste contemporaneamente, controllandone il movimento lungo percorsi e velocità diversi. Se specifichi un KeyFrame diverso per ogni visualizzazione, puoi coreografare animazioni avanzate che animano più visualizzazioni con MotionLayout.
10. Eventi di trascinamento e percorsi complessi
In questo passaggio esplorerai l'utilizzo di OnSwipe con percorsi complessi. Finora, l'animazione della luna è stata attivata da un listener OnClick e viene eseguita per una durata fissa.
Per controllare le animazioni con percorsi complessi utilizzando OnSwipe, come l'animazione della luna che hai creato nei passaggi precedenti, è necessario capire come funziona OnSwipe.
Passaggio 1: esplora il comportamento di OnSwipe
- Apri
xml/step7.xmle trova la dichiarazioneOnSwipeesistente.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- Esegui l'app sul tuo dispositivo e vai al passaggio 7. Prova a produrre un'animazione fluida trascinando la luna lungo il percorso dell'arco.
Quando esegui questa animazione, non ha un bell'aspetto. Dopo che la luna raggiunge la parte superiore dell'arco, inizia a saltare.

Per capire il bug, considera cosa succede quando l'utente tocca appena sotto la parte superiore dell'arco. Poiché il tag OnSwipe ha un valore motion:touchAnchorSide="bottom", MotionLayout cercherà di mantenere costante la distanza tra il dito e la parte inferiore della visualizzazione durante l'animazione.
Tuttavia, poiché la parte inferiore della luna non va sempre nella stessa direzione, ma sale e poi scende, MotionLayout non sa cosa fare quando l'utente ha appena superato la parte superiore dell'arco. Per tenerne conto, dato che stai monitorando la parte inferiore della luna, dove deve essere posizionata quando l'utente tocca qui?

Passaggio 2: utilizza il lato destro
Per evitare bug come questo, è importante scegliere sempre un touchAnchorId e un touchAnchorSide che progrediscano sempre in una direzione per tutta la durata dell'animazione.
In questa animazione, sia il lato right che il lato left della luna si sposteranno sullo schermo in una direzione.
Tuttavia, sia bottom che top invertiranno la direzione. Quando OnSwipe tenta di tracciarli, si confonde quando la direzione cambia.
- Per fare in modo che l'animazione segua gli eventi tocco, modifica
touchAnchorSideinright.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
Passaggio 3: utilizza dragDirection
Puoi anche combinare dragDirection con touchAnchorSide per far sì che una deviazione abbia una direzione diversa da quella normale. È comunque importante che il touchAnchorSide proceda in una sola direzione, ma puoi indicare a MotionLayout in quale direzione monitorare. Ad esempio, puoi mantenere touchAnchorSide="bottom", ma aggiungere dragDirection="dragRight". In questo modo, MotionLayout monitorerà la posizione della parte inferiore della visualizzazione, ma ne terrà conto solo quando si sposta a destra (ignora il movimento verticale). Quindi, anche se la parte inferiore si alza e si abbassa, l'animazione verrà comunque riprodotta correttamente con OnSwipe.
- Aggiorna
OnSwipeper monitorare correttamente il movimento della luna.
step7.xml
<!-- Using dragDirection to control the direction of drag tracking →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragRight"
/>
Prova
- Esegui di nuovo l'app e prova a trascinare la luna lungo tutto il percorso. Anche se segue un arco complesso,
MotionLayoutsarà in grado di far avanzare l'animazione in risposta agli eventi di scorrimento.

11. Esecuzione del movimento con il codice
MotionLayout può essere utilizzato per creare animazioni avanzate se utilizzato con CoordinatorLayout. In questo passaggio, creerai un'intestazione comprimibile utilizzando MotionLayout.
Passaggio 1: esplora il codice esistente
- Per iniziare, apri
layout/activity_step8.xml. - In
layout/activity_step8.xml, vedi che è già stato creato unCoordinatorLayoute unAppBarLayoutfunzionanti.
activity_step8.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
... >
...
</androidx.constraintlayout.motion.widget.MotionLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
...
motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Questo layout utilizza un CoordinatorLayout per condividere le informazioni di scorrimento tra NestedScrollView e AppBarLayout. Quindi, quando NestedScrollView scorre verso l'alto, comunica la modifica a AppBarLayout. In questo modo si implementa una barra degli strumenti comprimibile come questa su Android: lo scorrimento del testo viene "coordinato " con l'intestazione comprimibile.
La scena di movimento a cui punta @id/motion_layout è simile a quella dell'ultimo passaggio. Tuttavia, la dichiarazione OnSwipe è stata rimossa per consentirne l'utilizzo con CoordinatorLayout.
- Esegui l'app e vai al passaggio 8. Quando scorri il testo, la luna non si muove.
Passaggio 2: fai scorrere MotionLayout
- Per fare in modo che la visualizzazione
MotionLayoutscorra non appena scorreNestedScrollView, aggiungimotion:minHeightemotion:layout_scrollFlagsaMotionLayout.
activity_step8.xml
<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
motion:layoutDescription="@xml/step8"
motion:motionDebug="SHOW_PATH"
android:minHeight="80dp"
motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" >
- Esegui di nuovo l'app e vai al passaggio 8. Noti che
MotionLayoutsi comprime mentre scorri verso l'alto. Tuttavia, l'animazione non avanza ancora in base al comportamento di scorrimento.
Passaggio 3: sposta il movimento con il codice
- Apri
Step8Activity.kt. Modifica la funzionecoordinateMotion()per comunicare aMotionLayoutle modifiche alla posizione di scorrimento.
Step8Activity.kt
// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener
private fun coordinateMotion() {
val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
val motionLayout: MotionLayout = findViewById(R.id.motion_layout)
val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
motionLayout.progress = seekPosition
}
appBarLayout.addOnOffsetChangedListener(listener)
}
Questo codice registra un OnOffsetChangedListener che verrà chiamato ogni volta che l'utente scorre con l'offset di scorrimento corrente.
MotionLayout supporta la ricerca della transizione impostando la proprietà di avanzamento. Per convertire un valore verticalOffset in un avanzamento percentuale, dividi per l'intervallo di scorrimento totale.
Prova
- Esegui di nuovo il deployment dell'app ed esegui l'animazione del passaggio 8. Vedrai che
MotionLayoutfarà avanzare l'animazione in base alla posizione di scorrimento.

È possibile creare animazioni personalizzate della barra degli strumenti dinamica comprimibile utilizzando MotionLayout. Utilizzando una sequenza di KeyFrames puoi ottenere effetti molto audaci.
12. Complimenti
Questo codelab ha trattato l'API di base di MotionLayout.
Per vedere altri esempi di MotionLayout in pratica, consulta l'esempio ufficiale. E non dimenticare di consultare la documentazione.
Scopri di più
MotionLayout supporta ancora più funzionalità non trattate in questo codelab, come KeyCycle,, che consente di controllare percorsi o attributi con cicli ripetuti, e KeyTimeCycle,, che consente di animare in base all'ora. Dai un'occhiata agli esempi per ciascun tipo.
Per i link ad altri codelab di questo corso, consulta la pagina di destinazione dei codelab Advanced Android in Kotlin.