1. Prima di iniziare
Questo codelab fa parte del corso Advanced Android in Kotlin. Trarrai il massimo vantaggio da questo corso eseguendo i codelab in sequenza, ma non è obbligatorio. Tutti i codelab del corso sono elencati nella pagina di destinazione dei codelab Android in Kotlin avanzati.
MotionLayout
è una raccolta che ti consente di aggiungere immagini di movimento alla tua app per Android. È basato su ConstraintLayout,
e ti consente di animare tutto ciò che puoi creare utilizzando ConstraintLayout
.
Puoi utilizzare MotionLayout
per animare contemporaneamente posizione, dimensione, visibilità, alpha, colore, elevazione, rotazione e altri attributi di più viste. Utilizzando il codice XML dichiarativo puoi creare animazioni coordinate, che coinvolgono più viste, che sono difficili da ottenere nel codice.
Le animazioni sono un ottimo modo per migliorare l'esperienza di un'app. Puoi utilizzare le animazioni per:
- Mostra modifiche: l'animazione tra gli stati consente all'utente di tenere traccia in modo naturale delle modifiche nella UI.
- Attira l'attenzione: utilizza le animazioni per attirare l'attenzione sugli elementi importanti dell'interfaccia utente.
- Crea splendidi design: i movimenti efficaci rendono le app più eleganti.
Prerequisiti
Questo codelab è stato progettato per gli sviluppatori con una certa esperienza di sviluppo Android. Prima di tentare di completare questo codelab, dovresti:
- Sapere come creare un'app con un'attività, un layout di base ed eseguirla su un dispositivo o un emulatore usando Android Studio. Conosci
ConstraintLayout
. Leggi il codelab sul layout dei vincoli per saperne di più suConstraintLayout
.
Attività previste
- Definisci un'animazione con
ConstraintSets
eMotionLayout
- Creare animazioni in base a eventi di trascinamento
- Cambia l'animazione con
KeyPosition
- Modifica attributi con
KeyAttribute
- Eseguire animazioni con codice
- Applica l'animazione alle intestazioni comprimibili con
MotionLayout
Che cosa ti serve
- Android Studio 4.0 (l'editor
MotionLayout
funziona solo con questa versione di Android Studio).
2. Per iniziare
Per scaricare l'app di esempio, puoi:
... oppure clona il repository GitHub dalla riga di comando utilizzando questo comando:
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. Creazione di animazioni con MotionLayout
Innanzitutto, devi creare un'animazione che sposti la visualizzazione dall'inizio superiore dello schermo all'estremità inferiore in risposta ai clic degli utenti.
Per creare un'animazione dal codice di base, sono necessari i seguenti elementi principali:
- Un
MotionLayout,
che è una sottoclasse diConstraintLayout
. Devi specificare tutte le visualizzazioni da animate all'interno del tagMotionLayout
. - Un
MotionScene,
, cioè un file XML che descrive un'animazione perMotionLayout.
- Un elemento
Transition,
che fa parte diMotionScene
che specifica la durata dell'animazione, l'attivatore e la modalità di spostamento delle visualizzazioni. - Un elemento
ConstraintSet
che specifica i vincoli start e end 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à durante l'aggiunta dell'animazione. Per utilizzare MotionLayout
, aggiungi una vista MotionLayout
in cui useresti ConstraintLayout.
- In
res/layout
, apriactivity_step1.xml.
. Ecco unConstraintLayout
con un singoloImageView
di 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 eseguissi l'app ora vedresti la visualizzazione a stella non vincolata, il che significa che l'elemento verrebbe posizionato in una posizione sconosciuta. Android Studio ti invierà un avviso relativo alla mancanza di vincoli.
Passaggio 2: converti in layout Movimento
Per eseguire l'animazione con MotionLayout,
, devi convertire ConstraintLayout
in MotionLayout
.
Affinché il layout possa utilizzare una scena di movimento, deve puntare su di essa.
- Per farlo, apri la sezione di progettazione. In Android Studio 4.0, puoi aprire l'area di progettazione utilizzando l'icona di suddivisione o di progettazione in alto a destra quando esamini un file XML di layout.
- Una volta aperta la superficie di progettazione, fai clic con il pulsante destro del mouse sull'anteprima e seleziona Converti in layout Movimento.
In questo modo, il tag ConstraintLayout
viene sostituito con un tag MotionLayout
e viene aggiunto un motion:layoutDescription
al tag MotionLayout
che rimanda 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 in MotionLayout
, nella superficie di progettazione viene visualizzato Editor movimento
L'editor di movimento contiene tre nuovi elementi UI:
- Panoramica: si tratta di una selezione modale che consente di selezionare diverse parti dell'animazione. In questa immagine è selezionato
start
ConstraintSet
. Puoi anche selezionare la transizione trastart
eend
facendo clic sulla freccia tra i due elementi. - Sezione: sotto la panoramica è presente una finestra di sezione che cambia in base all'elemento della panoramica attualmente selezionato. In questa immagine, le informazioni
start
perConstraintSet
vengono visualizzate nella finestra di selezione. - Attributo: il riquadro degli attributi mostra e consente di modificare gli attributi dell'elemento attualmente selezionato dalla finestra Panoramica o dalla finestra di selezione. In questa immagine, vengono mostrati gli attributi per
start
ConstraintSet
.
Passaggio 3: definisci i vincoli di inizio e fine
Tutte le animazioni possono essere definite in termini di inizio e fine. All'inizio viene descritto l'aspetto della schermata prima dell'animazione, mentre alla fine viene descritto l'aspetto della schermata al termine dell'animazione. MotionLayout
si occupa di capire come eseguire l'animazione tra lo stato iniziale e quello finale (nel tempo).
MotionScene
utilizza un tag ConstraintSet
per definire gli stati di inizio e di fine. ConstraintSet
è come sembra, un insieme di vincoli che possono essere applicati alle visualizzazioni. Sono inclusi i vincoli di larghezza, altezza e ConstraintLayout
. Include anche alcuni attributi come alpha
. Non contiene le visualizzazioni stesse, ma solo i relativi vincoli.
Tutti i vincoli specificati in ConstraintSet
sostituiranno quelli specificati nel file di layout. Se definisci i vincoli sia nel layout sia in MotionScene
, vengono applicati solo i vincoli in MotionScene
.
In questo passaggio, bloccherai la visualizzazione a stella affinché inizi nella parte superiore dello schermo e termini nella parte inferiore dello schermo.
Puoi completare questo passaggio usando l'editor di movimento o modificando direttamente il testo di activity_step1_scene.xml
.
- Seleziona
start
VincoloSet nel riquadro Panoramica
- Nel riquadro di selezione, seleziona
red_star
. Al momento mostra 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_star
mostristart
quando selezioniConstraintSet
start
nel riquadro Panoramica. - Nel riquadro Attributi, con
red_star
selezionato inConstraintSet
distart
, aggiungi un vincolo in alto e inizia facendo clic sui pulsanti + blu.
- Apri
xml/activity_step1_scene.xml
per vedere 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>
L'elemento ConstraintSet
ha un valore id
pari a @id/start
e specifica tutti i vincoli da applicare a tutte le viste in MotionLayout
. Poiché questo MotionLayout
ha una sola visualizzazione, ne richiede una sola Constraint
.
L'elemento Constraint
all'interno di ConstraintSet
specifica l'ID della vista che è vincolante, @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 che è stato applicato a un ImageView
.
Questo vincolo specifica l'altezza, la larghezza e gli altri due vincoli necessari per vincolare la vista red_star
all'inizio superiore dell'elemento principale.
- Seleziona il Set di vincoli
end
nel riquadro Panoramica.
- Segui gli stessi passaggi di prima per aggiungere un
Constraint
perred_star
inend
ConstraintSet
. - Per utilizzare l'editor di movimento per completare questo passaggio, aggiungi un vincolo a
bottom
eend
facendo clic sui pulsanti blu +.
- Il codice in XML è simile al seguente:
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>
Proprio come @id/start
, questo ConstraintSet
ha un singolo Constraint
su @id/red_star
. Questa volta lo vincola all'estremità inferiore dello schermo.
Non è necessario nominare @id/start
e @id/end
, ma è consigliabile.
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 deve essere eseguita l'animazione o come animarla trascinando le visualizzazioni.
- Durante la creazione del file MotionScene, Motion Editor ha creato una transizione per noi per impostazione predefinita. Apri
activity_step1_scene.xml
per vedere 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. Osservando ogni attributo:
constraintSetStart
verrà applicato alle visualizzazioni all'avvio dell'animazione.constraintSetEnd
verrà applicato alle visualizzazioni alla fine dell'animazione.duration
specifica la durata dell'animazione in millisecondi.
MotionLayout
quindi creerà un percorso tra i vincoli di inizio e fine e lo anima per la durata specificata.
Passaggio 5: visualizza l'anteprima dell'animazione in Motion Editor
Animazione:video della riproduzione di un'anteprima della transizione in Motion Editor
- Apri Motion Editor e seleziona la transizione facendo clic sulla freccia tra
start
eend
nel riquadro Panoramica.
- Quando è selezionata una transizione, il riquadro di selezione mostra i controlli di riproduzione e una barra di scorrimento. Fai clic su Riproduci o trascina la posizione corrente per visualizzare l'anteprima dell'animazione.
Passaggio 6: aggiungi un gestore dei clic
Devi trovare 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 Panoramica.
- Fai clic su Crea gestore di clic o scorrimento nella barra degli strumenti del riquadro Panoramica . Viene aggiunto un gestore che avvierà una transizione.
- Seleziona Click Identifier dal popup.
- Cambia la View To Click in
red_star
.
- Fai clic su Aggiungi: il gestore dei clic è rappresentato da un piccolo puntino nella transizione dell'editor del movimento.
- Con la transizione selezionata nel riquadro Panoramica, aggiungi un attributo
clickAction
ditoggle
al gestore OnClick che hai appena aggiunto nel riquadro degli attributi.
- Apri
activity_step1_scene.xml
per vedere 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>
. Osservando ogni attributo:
targetId
è la visualizzazione per cui tenere d'occhio i clic.clickAction
ditoggle
passerà dallo stato iniziale a quello finale al clic. Puoi visualizzare altre opzioni perclickAction
nella documentazione.
- Esegui il codice, fai clic sul Passaggio 1, poi sulla stella rossa e visualizza l'animazione.
Passaggio 5: le animazioni in azione
Esegui l'app. Dovresti vedere l'animazione quando fai clic sulla stella.
Il file della scena di movimento completato definisce un Transition
che punta all'inizio e alla fine di ConstraintSet
.
All'inizio dell'animazione (@id/start
), l'icona a forma di stella è bloccata nella parte superiore dello schermo. Alla fine dell'animazione (@id/end
) l'icona a forma di stella è bloccata nella parte 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 risponda a un evento di trascinamento dell'utente (quando l'utente fa scorrere lo schermo) per eseguire l'animazione. MotionLayout
supporta il monitoraggio degli eventi di tocco per spostare le visualizzazioni, nonché i gesti di scorrimento basati sulla fisica per rendere fluido il movimento.
Passaggio 1: controlla il codice iniziale
- Per iniziare, apri il file di layout
activity_step2.xml
che contiene un fileMotionLayout
esistente. 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 icone con tre stelle non sono vincolate nel layout perché saranno animate nella scena in movimento.
Ai crediti TextView
sono applicati vincoli perché rimane nella stessa posizione per l'intera animazione e non modifica alcun attributo.
Passaggio 2: crea l'animazione della scena
Proprio come l'ultima animazione, l'animazione sarà definita da un inizio e una fine ConstraintSet,
e da un Transition
.
Definisci il valore ConstraintSet iniziale
- Apri la scena di movimento
xml/step2.xml
per 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 valorealpha
pari a0.0
, che indica 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 campo ConstraintSet
, specificherai un Constraint
per ciascuna delle stelle. Ogni vincolo verrà applicato da MotionLayout
all'inizio dell'animazione.
Ogni visualizzazione a stella è centrata nella parte inferiore dello schermo tramite i punti di inizio, fine e basso. Le due stelle @id/left_star
e @id/right_star
hanno entrambe un valore alpha aggiuntivo che le rende invisibili e che verrà applicato all'inizio dell'animazione.
Gli insiemi di vincoli start
e end
definiscono l'inizio e la fine dell'animazione. Un vincolo all'inizio, ad esempio motion:layout_constraintStart_toStartOf
, impedirà all'inizio di una vista di passare a un'altra. Inizialmente ciò può creare confusione, perché il nome start
viene utilizzato sia per sia per entrambi i casi nel contesto dei vincoli. Per fare questa distinzione, start
in layout_constraintStart
fa riferimento alla parola "inizio" della visualizzazione, ovvero da sinistra in una lingua da sinistra a destra e da destra a sinistra in una lingua. L'insieme di vincoli start
fa riferimento all'inizio dell'animazione.
Definire il valore ConstraintSet finale
- Definisci il vincolo finale per utilizzare una catena per posizionare tutte e tre le stelle insieme al di sotto di
@id/credits
. Inoltre, verrà impostato il valore finale delle stelle (alpha
) delle 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 distenderanno verso l'alto e verso l'alto dal centro mentre si animano.
Inoltre, poiché la proprietà alpha
è impostata su @id/right_start
e @id/left_star
in entrambi i tipi di ConstraintSets
, entrambe le viste appariranno dissolventi man mano che l'animazione procede.
Animazione basata sullo scorrimento dell'utente
MotionLayout
può monitorare gli eventi di trascinamento dell'utente o un gesto di scorrimento per creare una "fling" basata sulla fisica l'animazione. Ciò significa che le visualizzazioni continueranno se l'utente le lancia e rallenterà come un oggetto fisico quando rotola su una superficie. Puoi aggiungere questo tipo di animazione con un tag OnSwipe
in Transition
.
- Sostituisci TODO per l'aggiunta di un tag
OnSwipe
con<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 monitorata che si sposta in risposta al tocco.MotionLayout
manterrà la visualizzazione alla stessa distanza dal dito che sta scorrendo.touchAnchorSide
determina quale lato della visualizzazione deve essere monitorato. Questo aspetto è importante per le viste che si ridimensionano, seguono percorsi complessi o hanno un lato che si muove più velocemente dell'altro.dragDirection
determina la direzione importante per questa animazione (su, giù, sinistra o destra).
Quando MotionLayout
rimane in ascolto degli eventi di trascinamento, il listener verrà registrato nella visualizzazione MotionLayout
e non nella visualizzazione specificata da touchAnchorId
. Quando un utente avvia un gesto in qualsiasi punto dello schermo, MotionLayout
manterrà costante la distanza tra il suo dito e il touchAnchorSide
della visualizzazione touchAnchorId
. Se toccano 100 dp dal lato dell'ancoraggio, ad esempio, MotionLayout
manterrà quel lato a 100 dp dal dito per l'intera animazione.
Prova
- Esegui di nuovo l'app e apri la schermata del Passaggio 2. Verrà visualizzata l'animazione.
- Prova a "ballare" o sollevando il dito a metà dell'animazione per scoprire come
MotionLayout
mostra animazioni fluide basate sulla fisica.
MotionLayout
può animare design molto diversi utilizzando le funzionalità di ConstraintLayout
per creare effetti intensi.
In questa animazione, per iniziare, tutte e tre le visualizzazioni sono posizionate rispetto all'elemento principale nella parte inferiore dello schermo. Alla fine, le tre viste sono posizionate rispetto 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 seguito da una visualizzazione tra l'inizio e la fine utilizzando un KeyPosition
.
Passaggio 1: esplora il codice esistente
- Apri
layout/activity_step3.xml
exml/step3.xml
per vedere il layout e la scena di movimento esistenti.ImageView
eTextView
mostrano la luna e il testo dei crediti. - Apri il file della scena di movimento (
xml/step3.xml
). Puoi notare che è definito unTransition
da@id/start
a@id/end
. L'animazione sposta l'immagine della luna dalla parte in basso a sinistra dello schermo a quella in basso a destra usando dueConstraintSets
. Il testo dei crediti va in dissolvenza daalpha="0.0"
aalpha="1.0"
mentre la luna si muove. - Esegui l'app ora e seleziona Passaggio 3. Vedrai che la luna segue un percorso lineare (o una linea retta) dall'inizio alla fine quando fai clic sulla luna.
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 tracciare il percorso di animazione di ogni vista. Ciò è utile per visualizzare l'animazione e per regolare con precisione i piccoli dettagli del movimento.
- Per attivare i percorsi di debug, apri
layout/activity_step3.xml
e 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 dei percorsi, quando esegui nuovamente l'app, i percorsi di tutte le visualizzazioni verranno mostrati con una linea tratteggiata.
- I cerchi rappresentano la posizione iniziale o finale di una visualizzazione.
- Le linee rappresentano il percorso di una vista.
- I diamanti rappresentano un
KeyPosition
che 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 ConstraintSet
di inizio e di fine, che definisce l'aspetto della schermata prima dell'inizio e dopo il completamento dell'animazione. Per impostazione predefinita, MotionLayout
traccia un percorso lineare (una linea retta) tra la posizione iniziale e quella finale di ogni vista che cambia posizione.
Per creare percorsi complessi come l'arco lunare in questo esempio, MotionLayout
utilizza un KeyPosition
per modificare il percorso di una vista tra l'inizio e la fine.
- Apri
xml/step3.xml
e aggiungiKeyPosition
alla scena. Il tagKeyPosition
è inserito 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>
KeyFrameSet
è un elemento secondario di Transition
ed è un insieme di tutti gli elementi KeyFrames
, ad esempio KeyPosition
, da applicare durante la transizione.
Poiché MotionLayout
sta calcolando il percorso per la luna tra l'inizio e la fine, modifica il percorso in base al valore KeyPosition
specificato nelle KeyFrameSet
. Esegui di nuovo l'app per vedere come questo modifica il percorso.
Un elemento KeyPosition
ha diversi attributi che descrivono il modo in cui modifica il percorso. I più importanti sono:
framePosition
è un numero compreso tra 0 e 100. Definisce quando deve essere applicato questoKeyPosition
nell'animazione; 1 corrisponde all'1% dell'animazione e 99 al 99% dell'animazione. Quindi, se il valore è 50, lo applichi direttamente al centro.motionTarget
è la vista per cui questoKeyPosition
modifica il percorso.keyPositionType
è il modo in cui questoKeyPosition
modifica il percorso. Può essereparentRelative
,pathRelative
odeltaRelative
(come spiegato nel passaggio successivo).percentX | percentY
è l'importo da modificare per il percorso inframePosition
(valori compresi tra 0,0 e 1,0, con valori negativi e valori >1 consentiti).
Si pensa così: "All'interno di framePosition
modifica il percorso di motionTarget
spostandolo di percentX
o di percentY
in base alle coordinate stabilite da keyPositionType
."
Per impostazione predefinita, MotionLayout
arrotonda tutti gli angoli introdotti modificando il percorso. Se osservi l'animazione che hai appena creato, puoi vedere che la luna segue un percorso curvo in corrispondenza della curva. Questo è ciò che vuoi ottenere per la maggior parte delle animazioni. In caso contrario, puoi specificare l'attributo curveFit
per personalizzarlo.
Prova
Se riavvii l'app, viene visualizzata l'animazione di questo passaggio.
La luna segue un arco perché passa attraverso un valore KeyPosition
specificato nell'elemento Transition
.
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
Puoi leggere questo KeyPosition
come: "In framePosition 50
(a metà dell'animazione) modifica il percorso di motionTarget
@id/moon
spostandolo di 50% Y
(a metà dello schermo) in base alle coordinate determinate da parentRelative
(l'intero MotionLayout
)."
Quindi, a metà dell'animazione, la luna deve passare attraverso un KeyPosition
posizionato al 50% in basso sullo schermo. Questo KeyPosition
non modifica affatto il movimento X, quindi la luna passerà comunque dall'inizio alla fine in orizzontale. MotionLayout
individuerà un percorso fluido che attraversa questo KeyPosition
passando dall'inizio alla fine e viceversa.
Se guardi attentamente, il testo dei crediti è vincolato dalla posizione della luna. Perché non si muove anche in verticale?
<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
A quanto pare, anche se si sta modificando il percorso della luna, le posizioni di inizio e fine della luna non la spostano affatto in verticale. KeyPosition
non modifica la posizione iniziale o finale, quindi il testo dei crediti è vincolato alla posizione finale finale della luna.
Se vuoi che i crediti si spostino con la luna, puoi aggiungere KeyPosition
ai crediti o modificare i vincoli di inizio su @id/credits
.
Nella sezione successiva, analizzerai in dettaglio i diversi tipi di keyPositionType
in MotionLayout
.
6. Informazioni su keyPositionType
Nell'ultimo passaggio hai utilizzato un tipo keyPosition
di parentRelative
per eseguire l'offset del percorso del 50% dello schermo. L'attributo keyPositionType
determina il modo in cui MotionLayout modificherà il percorso secondo percentX
o percentY
.
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Sono possibili tre diversi tipi di keyPosition
: parentRelative
, pathRelative
e deltaRelative
. Se specifichi un tipo, verrà modificato il sistema di coordinate con cui vengono calcolati percentX
e percentY
.
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.
I sistemi di coordinate di MotionLayout
sono un sistema di coordinate cartesiano. ovvero gli assi X e Y sono definiti da due rette perpendicolari. La differenza principale è il punto sullo schermo in cui si inserisce l'asse X (l'asse Y è sempre perpendicolare all'asse X).
Tutti i sistemi di coordinate in MotionLayout
utilizzano valori compresi tra 0.0
e 1.0
su entrambi gli assi X e Y. Consentono valori negativi e valori superiori a 1.0
. Ad esempio, un valore percentX
di -2.0
significa che devi andare nella direzione opposta dell'asse X due volte.
Se ti sembra un po' troppo legato alla lezione di algebra, dai un'occhiata alle immagini qui sotto.
Coordinate padreParent
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 sposta sull'intero MotionLayout
, come l'arco lunare in questo esempio.
Tuttavia, se vuoi modificare un percorso relativo al movimento, ad esempio curvarlo leggermente, gli altri due sistemi di coordinate sono la scelta migliore.
Coordinate deltarelative
Delta è un termine matematico che indica la variazione, quindi deltaRelative
è un modo per dire "cambia relativo". Nelle deltaRelative
coordinate(0,0)
è la posizione iniziale della vista, mentre (1,1)
è la posizione finale. Gli assi X e Y sono allineati allo schermo.
L'asse X sullo schermo è sempre orizzontale e l'asse Y è sempre verticale sullo schermo. Rispetto a parentRelative
, la differenza principale è che le coordinate descrivono solo la parte dello schermo su 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 solo il suo movimento verticale (Y) al 50% e continua ad animarsi orizzontalmente (X).
Coordinate relative
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é vuoi riceverlo? È abbastanza sorprendente a prima vista, soprattutto perché questo sistema di coordinate non è nemmeno allineato a quello sullo schermo.
A quanto pare, pathRelative
è davvero utile per alcune cose.
- Accelerare, rallentare o interrompere la visualizzazione durante una parte dell'animazione. Poiché la dimensione X corrisponderà sempre esattamente al percorso seguito dalla visualizzazione, puoi utilizzare un
KeyPosition
pathRelative
per modificare la modalitàframePosition
raggiunta da un determinato punto del percorso. Di conseguenza, unKeyPosition
inframePosition="50"
con unpercentX="0.1"
fa sì che l'animazione impieghi il 50% del tempo per percorrere il primo 10% del movimento. - Aggiunta di un sottile arco a un percorso. Poiché la dimensione Y è sempre perpendicolare al movimento, la modifica di Y cambia il percorso in curva rispetto al movimento complessivo.
- Aggiungi una seconda dimensione quando
deltaRelative
non funziona. Per il movimento completamente orizzontale e verticale,deltaRelative
creerà solo una dimensione utile. Tuttavia,pathRelative
creerà sempre coordinate X e Y utilizzabili.
Nel passaggio successivo imparerai a creare percorsi ancora più complessi utilizzando più di un KeyPosition
.
7. Costruire percorsi complessi
Osservando l'animazione che hai creato nell'ultimo passaggio, si crea una curva fluida, ma la forma potrebbe essere più simile alla luna.
Modificare un percorso con più elementi KeyPosition
MotionLayout
può modificare ulteriormente un percorso definendo il numero di KeyPosition
necessario per ottenere qualsiasi movimento. Per questa animazione creerai un arco, ma se vuoi potresti far saltare la luna su e giù al centro dello schermo.
- Apri
xml/step4.xml
. Come vedi, ha le stesse visualizzazioni e loKeyFrame
che hai aggiunto nell'ultimo passaggio. - Per arrotondare la parte superiore della curva, aggiungi altri due
KeyPositions
al percorso di@id/moon
, uno appena prima che raggiunga la cima 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 sì che @id/moon
si muova lungo un percorso che si trova al 60% dalla parte superiore dello schermo. Combinato con il valore attuale di KeyPosition
al 50%, viene creato un arco uniforme che la luna farà seguire.
In MotionLayout
, puoi aggiungere tutti i KeyPositions
necessari per ottenere il percorso di animazione che preferisci. MotionLayout
applicherà ogni KeyPosition
al framePosition
specificato e capirà come creare un movimento fluido che attraversa tutti i KeyPositions
.
Prova
- Esegui di nuovo l'app. Vai al passaggio 4 per vedere come funziona l'animazione. Quando fai clic sulla luna, questo segue il percorso dall'inizio alla fine, attraversando ogni
KeyPosition
specificato nell'KeyFrameSet
.
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 usando solo KeyPosition
.
Questo è 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. Modificare gli 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 progredisce. MotionLayout
supporta l'animazione di molti attributi in qualsiasi vista utilizzando un KeyAttribute
.
In questo passaggio, utilizzerai KeyAttribute
per scalare e ruotare la luna. Utilizzerai anche un KeyAttribute
per ritardare la comparsa del testo finché la luna non avrà quasi completato il suo viaggio.
Passaggio 1: ridimensiona e ruota con KeyAttribute
- Apri
xml/step5.xml
, che contiene la stessa animazione che hai creato nell'ultimo passaggio. Per maggiore varietà, questa schermata utilizza un'immagine dello spazio diversa come sfondo. - Per far sì che la luna si espanda di dimensioni e ruoti, aggiungi due tag
KeyAttribute
inKeyFrameSet
inkeyFrame="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% avverrà nella parte superiore dell'arco e determina il raddoppiamento delle dimensioni della vista e la rotazione di -360 gradi (o un cerchio intero). Il secondo KeyAttribute
terminerà la seconda rotazione a -720 gradi (due cerchi completi) e ridurrà le dimensioni al valore normale poiché i valori di scaleX
e scaleY
saranno impostati su 1,0 per impostazione predefinita.
Proprio come un KeyPosition
, un KeyAttribute
utilizza i framePosition
e motionTarget
per specificare quando applicare l'KeyFrame
e la vista da modificare. MotionLayout
eseguirà l'interpolazione tra KeyPositions
per creare animazioni fluide.
KeyAttributes
supporta gli attributi che possono essere applicati a tutte le viste. Supportano la modifica degli attributi di base, ad esempio visibility
, alpha
o elevation
. Puoi anche modificare la rotazione come stai facendo qui, ruotare tre dimensioni con rotateX
e rotateY
, ridimensionare le dimensioni con scaleX
e scaleY
o tradurre la posizione della vista in X, Y o Z.
Passaggio 2: posticipa la visualizzazione dei riconoscimenti
Uno degli obiettivi di questo passaggio è aggiornare l'animazione in modo che il testo dei riconoscimenti non venga visualizzato fino al completamento dell'animazione.
- Per ritardare la visualizzazione dei crediti, definisci un altro
KeyAttribute
in modo chealpha
rimanga pari a 0 fino al giornokeyPosition="85"
.MotionLayout
eseguirà comunque la transizione da 0 a 100 alpha senza problemi, 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 valore di alpha
di @id/credits
a 0,0 per il primo 85% dell'animazione. Dal momento che inizia con un alpha pari a 0, significa che sarà invisibile per il primo 85% dell'animazione.
L'effetto finale di questo KeyAttribute
è che i riconoscimenti vengono visualizzati verso la fine dell'animazione. In questo modo sembreranno che siano coordinati con la luna che si assesta nell'angolo destro dello schermo.
Posticipando le animazioni in una vista mentre un'altra si muove in questo modo, puoi creare animazioni straordinarie e 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, seguirà il percorso dall'inizio alla fine, passando per ogni
KeyAttribute
specificato nell'KeyFrameSet
.
Poiché ruoti la luna di due cerchi pieni, ora eseguirà un doppio capovolgimento e i riconoscimenti ritarderanno la loro visualizzazione fino al termine dell'animazione.
Esplora in autonomia
Prima di passare al tipo finale di KeyFrame
, prova a modificare altri attributi standard in KeyAttributes
. Ad esempio, prova a modificare rotation
in rotationX
per vedere quale animazione produce.
Di seguito è riportato un elenco degli attributi standard che puoi provare a utilizzare:
android:visibility
android:alpha
android:elevation
android:rotation
android:rotationX
android:rotationY
android:scaleX
android:scaleY
android:translationX
android:translationY
android:translationZ
9. Modifica degli attributi personalizzati
Le animazioni avanzate implicano 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, tu utilizzi un CustomAttribute
per specificare qualsiasi altro attributo.
È possibile utilizzare un CustomAttribute
per impostare qualsiasi valore che abbia un setter. Ad esempio, puoi impostare backgroundColor su una vista utilizzando un CustomAttribute
. MotionLayout
utilizzerà la riflessione 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 e creare l'animazione mostrata di seguito.
Definire gli attributi personalizzati
- Per iniziare, apri
xml/step6.xml
, che contiene la stessa animazione che hai creato nell'ultimo passaggio. - Per fare in modo che il colore della luna cambi, aggiungi due
KeyAttribute
conCustomAttribute
inKeyFrameSet
alle orekeyFrame="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 CustomAttribute
all'interno di un KeyAttribute
. Il CustomAttribute
verrà applicato al framePosition
specificato da KeyAttribute
.
All'interno di CustomAttribute
, devi specificare un valore attributeName
e un valore da impostare.
motion:attributeName
è il nome del setter che verrà chiamato da questo attributo personalizzato. In questo esempio verrà chiamatosetColorFilter
suDrawable
.motion:custom*Value
è un valore personalizzato del tipo indicato nel nome, in questo esempio il valore personalizzato è un colore specificato.
I valori personalizzati possono avere i seguenti tipi:
- Colore
- Numero intero
- Float
- Stringa
- Dimensione
- Booleano
Utilizzando questa API, MotionLayout
può animare tutto ciò che fornisce un setter su qualsiasi vista.
Prova
- Esegui di nuovo l'app e vai al passaggio 6 per vedere l'animazione in azione. Quando fai clic sulla luna, seguirà il percorso dall'inizio alla fine, passando per ogni
KeyAttribute
specificato nell'KeyFrameSet
.
Quando aggiungi altri KeyFrames
, MotionLayout
cambia il percorso della luna da una linea retta a una curva complessa, aggiungendo un doppio capovolgimento, ridimensionamento e un cambio di colore a metà dell'animazione.
Nelle animazioni reali, spesso puoi animare diverse visualizzazioni contemporaneamente, controllandone il movimento lungo percorsi e velocità diversi. Specificando un KeyFrame
diverso per ogni visualizzazione, è possibile creare 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 con OnSwipe
, come l'animazione della luna che hai creato negli ultimi passaggi, devi comprendere come funziona OnSwipe
.
Passaggio 1: esplora il comportamento OnScorrimento
- Apri
xml/step7.xml
e trova la dichiarazioneOnSwipe
esistente.
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. Verifica se riesci a produrre un'animazione fluida trascinando la luna lungo il percorso dell'arco.
Quando esegui questa animazione, il risultato non sarà molto buono. Quando la luna raggiunge la cima dell'arco, inizia a saltellare.
Per capire il bug, pensa a cosa succede quando l'utente tocca appena sotto la parte superiore dell'arco. Poiché il tag OnSwipe
ha un MotionLayout
di motion:touchAnchorSide="bottom"
, cercherà di rendere costante la distanza tra il dito e la parte inferiore della visualizzazione per tutta l'animazione.
Ma, poiché la parte inferiore della luna non sempre va nella stessa direzione, sale e poi di nuovo giù, MotionLayout
non sa cosa fare quando l'utente ha appena superato la parte superiore dell'arco. Dato che stai monitorando la parte inferiore della luna, dove dovrebbe essere posizionato quando l'utente tocca questo punto?
Passaggio 2: usa il lato destro
Per evitare bug come questo, è importante scegliere sempre touchAnchorId
e touchAnchorSide
che avanzano sempre in una direzione per tutta la durata dell'intera animazione.
In questa animazione, sia il lato right
sia il lato left
della luna avanzeranno sullo schermo in una direzione.
Tuttavia, sia bottom
che top
andranno a invertire la direzione. Quando OnSwipe
tenta di monitorarli, si crea confusione se la direzione cambia.
- Per fare in modo che l'animazione segua gli eventi touch, modifica
touchAnchorSide
inright
.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
Passaggio 3: utilizza la direzione di trascinamento
Puoi anche combinare dragDirection
con touchAnchorSide
per creare una direzione laterale diversa rispetto a quella che faresti normalmente. È comunque importante che touchAnchorSide
avanza in una sola direzione, ma puoi indicare a MotionLayout
la direzione da monitorare. Ad esempio, puoi mantenere touchAnchorSide="bottom"
, ma aggiungere dragDirection="dragRight"
. Questo fa sì che MotionLayout
monitori la posizione della parte inferiore della vista, ma ne considererà la posizione solo quando si sposta verso destra (ignora il movimento verticale). In questo modo, anche se le parti in basso e in alto si animano comunque correttamente con OnSwipe
.
- Aggiorna
OnSwipe
per 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 l'intero percorso. Anche se segue un arco complesso,
MotionLayout
sarà in grado di far avanzare l'animazione in risposta agli eventi di scorrimento.
11. Esecuzione di movimenti con codice
L'elemento MotionLayout
può essere utilizzato per creare animazioni avanzate 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
, puoi vedere che sono già stati creatiCoordinatorLayout
eAppBarLayout
funzionanti.
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 elemento CoordinatorLayout
per condividere le informazioni di scorrimento tra NestedScrollView
e AppBarLayout
. Pertanto, quando NestedScrollView
scorre verso l'alto, indicherà a AppBarLayout
la modifica. In questo modo implementi una barra degli strumenti comprimibile come questa su Android: lo scorrimento del testo sarà "coordinato" con l'intestazione compressa.
La scena in movimento a cui punta @id/motion_layout
è simile alla scena in movimento nell'ultimo passaggio. Tuttavia, la dichiarazione OnSwipe
è stata rimossa per consentirne il funzionamento con CoordinatorLayout
.
- Esegui l'app e vai al passaggio 8. Quando scorri il testo, la luna non si muove.
Passaggio 2: fai scorrere il layout Movimento
- Per fare scorrere la visualizzazione
MotionLayout
non appena l'NestedScrollView
scorre, aggiungimotion:minHeight
emotion:layout_scrollFlags
allaMotionLayout
.
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. Come vedi, la barra
MotionLayout
si 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 indicare aMotionLayout
i cambiamenti nella 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 registrerà un OnOffsetChangedListener
che verrà chiamato ogni volta che l'utente scorrerà con l'offset di scorrimento corrente.
MotionLayout
supporta la ricerca della transizione impostando la proprietà di avanzamento. Per convertire da verticalOffset
a una percentuale di avanzamento, dividi il risultato per l'intervallo di scorrimento totale.
Prova
- Esegui di nuovo il deployment dell'app ed esegui l'animazione Passaggio 8. Puoi notare che
MotionLayout
farà avanzare l'animazione in base alla posizione di scorrimento.
È possibile creare animazioni dinamiche di compressione della barra degli strumenti personalizzate utilizzando MotionLayout
. Utilizzando una sequenza di KeyFrames
puoi ottenere effetti molto audaci.
12. Complimenti
Questo codelab ha riguardato l'API di base di MotionLayout
.
Per vedere altri esempi pratici di MotionLayout
, guarda l'esempio ufficiale. Non perderti la documentazione.
Scopri di più
MotionLayout
supporta ancora più funzionalità non trattate in questo codelab, come KeyCycle,
, che consente di controllare i percorsi o gli attributi con cicli ricorrenti, e KeyTimeCycle,
, che consente di animare in base all'orologio. Dai un'occhiata agli esempi per ciascun suggerimento.
Per i link ad altri codelab di questo corso, consulta la pagina di destinazione avanzata dei codelab Android in Kotlin.