Gestisci l'animazione e l'animazione dei widget con MotionLayout

MotionLayout è un tipo di layout che ti consente di gestire le animazioni di movimento e widget nella tua app. MotionLayout è una sottoclasse di ConstraintLayout e si basa sulle sue funzionalità di layout. Nell'ambito della libreria ConstraintLayout, MotionLayout è disponibile come libreria di supporto.

MotionLayout colma il divario tra le transizioni di layout e la gestione complessa dei movimenti, offrendo un mix di funzionalità tra il framework di animazione delle proprietà, TransitionManager e CoordinatorLayout.

Figura 1. Movimenti semplici controllati al tocco.

Oltre a descrivere le transizioni tra i layout, MotionLayout consente di animare qualsiasi proprietà del layout. Inoltre, supporta intrinsecamente le transizioni scorrevoli. Ciò significa che puoi mostrare immediatamente qualsiasi punto all'interno della transizione in base a una condizione, ad esempio l'input tocco. MotionLayout supporta anche i keyframe, che consentono transizioni completamente personalizzate in base alle tue esigenze.

MotionLayout è un valore completamente dichiarativo, vale a dire che puoi descrivere qualsiasi transizione in XML, indipendentemente dalla complessità.

Considerazioni sul design

MotionLayout è progettato per spostare, ridimensionare e animare gli elementi dell'interfaccia utente con cui interagiscono gli utenti, come pulsanti e barre dei titoli. Non utilizzare il movimento nella tua app come effetto speciale senza costi. Usali per aiutare gli utenti a capire cosa fa la tua app. Per saperne di più sulla progettazione dell'app con il movimento, consulta la sezione Informazioni sul movimento di Material Design.

Inizia

Segui questi passaggi per iniziare a utilizzare MotionLayout nel tuo progetto.

  1. Aggiungi la dipendenza ConstraintLayout: per utilizzare MotionLayout nel tuo progetto, aggiungi la dipendenza ConstraintLayout 2.0 al file build.gradle della tua app. Se utilizzi AndroidX, aggiungi la seguente dipendenza:

    Alla moda

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.0-beta01"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01"
    }
    

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-beta01")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01")
    }
    
  2. Crea un file MotionLayout: MotionLayout è una sottoclasse di ConstraintLayout, quindi puoi trasformare qualsiasi ConstraintLayout esistente in un MotionLayout sostituendo il nome della classe nel file delle risorse di layout, come mostrato negli esempi seguenti:

    AndroidX

    <!-- before: ConstraintLayout -->
    <androidx.constraintlayout.widget.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <androidx.constraintlayout.motion.widget.MotionLayout .../>
              

    Libreria di supporto

    <!-- before: ConstraintLayout -->
    <android.support.constraint.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <android.support.constraint.motion.MotionLayout .../>
              

    Ecco un esempio completo di un file MotionLayout, che definisce il layout mostrato nella figura 1:

    AndroidX

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <androidx.constraintlayout.motion.widget.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </androidx.constraintlayout.motion.widget.MotionLayout>
            

    Libreria di supporto

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <android.support.constraint.motion.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </android.support.constraint.motion.MotionLayout>
            
  3. Crea una scena animata: nell'esempio MotionLayout precedente, l'attributo app:layoutDescription fa riferimento a una scena animata. Una scena in movimento è un file di risorse XML. All'interno del suo elemento radice <MotionScene>, una scena animata contiene tutte le descrizioni dei movimenti per il layout corrispondente. Per mantenere separate le informazioni sul layout dalle descrizioni del movimento, ogni MotionLayout fa riferimento a una scena di movimento distinta. Le definizioni nella scena in movimento hanno la precedenza su eventuali definizioni simili nel MotionLayout.

    Ecco un esempio di file di scena con movimento che descrive il movimento orizzontale di base nella figura 1:

    <?xml version="1.0" encoding="utf-8"?>
    <MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:motion="http://schemas.android.com/apk/res-auto">
    
        <Transition
            motion:constraintSetStart="@+id/start"
            motion:constraintSetEnd="@+id/end"
            motion:duration="1000">
            <OnSwipe
                motion:touchAnchorId="@+id/button"
                motion:touchAnchorSide="right"
                motion:dragDirection="dragRight" />
        </Transition>
    
        <ConstraintSet android:id="@+id/start">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginStart="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
        <ConstraintSet android:id="@+id/end">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginEnd="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
    </MotionScene>
        

    Nota:

    • <Transition> contiene la definizione di base del movimento.

      • motion:constraintSetStart e motion:constraintSetEnd sono riferimenti ai punti finali del movimento. Questi endpoint sono definiti negli elementi <ConstraintSet> più avanti nella scena di movimento.

      • motion:duration specifica il numero di millisecondi necessari per il completamento del movimento.

    • <OnSwipe> consente di creare controlli touch per il movimento.

      • motion:touchAnchorId si riferisce alla visualizzazione che l'utente può scorrere e trascinare.

      • motion:touchAnchorSide indica che la visualizzazione viene trascinata dal lato destro.

      • motion:dragDirection si riferisce alla direzione di avanzamento del trascinamento. Ad esempio, motion:dragDirection="dragRight" indica che il progresso aumenta man mano che la visualizzazione viene trascinata verso destra.

    • <ConstraintSet> è dove definisci i vari vincoli che descrivono il movimento. In questo esempio, viene definito un <ConstraintSet> per ogni endpoint del movimento. Questi endpoint sono centrati verticalmente utilizzando app:layout_constraintTop_toTopOf="parent" e app:layout_constraintBottom_toBottomOf="parent". Orizzontalmente, i punti finali si trovano ai lati più a sinistra e a destra dello schermo.

    Per una visione più dettagliata dei vari elementi supportati da una scena di movimento, vedi gli esempi di MotionLayout.

Attributi interpolati

All'interno di un file di scena animata, gli elementi ConstraintSet possono contenere attributi aggiuntivi che vengono interpolati durante la transizione. Oltre a position e bounds, i seguenti attributi vengono interpolati da MotionLayout:

  • alpha
  • visibility
  • elevation
  • rotation, rotationX e rotationY
  • translationX, translationY e translationZ
  • scaleX, scaleY

Attributi personalizzati

All'interno di un <Constraint>, puoi utilizzare l'elemento <CustomAttribute> per specificare una transizione per gli attributi che non sono semplicemente correlati alla posizione o agli attributi View.

<Constraint
    android:id="@+id/button" ...>
    <CustomAttribute
        motion:attributeName="backgroundColor"
        motion:customColorValue="#D81B60"/>
</Constraint>

Un <CustomAttribute> contiene due attributi:

  • motion:attributeName è obbligatorio e deve corrispondere a un oggetto con metodi getter e setter. Il getter e il setter devono corrispondere a un pattern specifico. Ad esempio, backgroundColor è supportato, poiché la visualizzazione ha metodi getBackgroundColor() e setBackgroundColor() sottostanti.
  • L'altro attributo da fornire si basa sul tipo di valore. Scegli tra i seguenti tipi supportati:
    • motion:customColorValue per i colori
    • motion:customIntegerValue per numeri interi
    • motion:customFloatValue per i float
    • motion:customStringValue per le stringhe
    • motion:customDimension per le dimensioni
    • motion:customBoolean per i valori booleani

Quando specifichi un attributo personalizzato, definisci i valori di endpoint sia negli elementi <ConstraintSet> inizio che <ConstraintSet> fine.

Cambia il colore dello sfondo

Basandoti sull'esempio precedente, supponiamo che tu voglia che i colori della visualizzazione cambino nel corso del movimento, come mostrato nella figura 2.

Figura 2. La visualizzazione cambia colore dello sfondo man mano che si sposta.

Aggiungi un elemento <CustomAttribute> a ogni elemento ConstraintSet, come mostrato nel seguente snippet di codice:

<ConstraintSet android:id="@+id/start">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#D81B60" />
    </Constraint>
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginEnd="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#9999FF" />
    </Constraint>
</ConstraintSet>

Attributi MotionLayout aggiuntivi

Oltre agli attributi nell'esempio precedente, MotionLayout ha altri attributi che potresti voler specificare:

  • app:applyMotionScene="boolean" indica se applicare la scena di movimento. Il valore predefinito di questo attributo è true.
  • app:showPaths="boolean" indica se mostrare i percorsi di movimento durante l'esecuzione del movimento. Il valore predefinito di questo attributo è false.
  • app:progress="float" consente di specificare in modo esplicito lo stato di avanzamento della transizione. Puoi utilizzare qualsiasi valore in virgola mobile da 0 (l'inizio della transizione) a 1 (la fine della transizione).
  • app:currentState="reference" ti consente di specificare un ConstraintSet specifico.
  • app:motionDebug ti consente di visualizzare ulteriori informazioni di debug sul movimento. I valori possibili sono "SHOW_PROGRESS", "SHOW_PATH" o "SHOW_ALL".

Risorse aggiuntive

Per ulteriori informazioni su MotionLayout, consulta le seguenti risorse: