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

MotionLayout è un tipo di layout che ti consente di gestire l'animazione e l'animazione dei widget nella tua app. MotionLayout è una sottoclasse di ConstraintLayout e si basa sulle sue funzionalità di layout avanzate. Poiché fa parte della libreria ConstraintLayout, MotionLayout è disponibile come libreria di supporto.

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

Figura 1. Movimento di base controllato tramite tocco.

Oltre a descrivere le transizioni tra i layout, MotionLayout consente di animare le proprietà del layout. Inoltre, supporta intrinsecamente le transizioni in ricerca. Questo significa che puoi mostrare subito qualsiasi punto della transizione in base ad alcune condizioni, ad esempio l'input tocco. MotionLayout supporta anche i fotogrammi chiave, consentendo transizioni completamente personalizzate in base alle tue esigenze.

MotionLayout è un'espressione completamente dichiarativa, il che significa che puoi descrivere qualsiasi transizione in XML, a prescindere dalla sua complessità.

Considerazioni sulla progettazione

MotionLayout è progettato per spostare, ridimensionare e animare gli elementi dell'interfaccia utente con cui interagiscono gli utenti, ad esempio pulsanti e barre dei titoli. Non usare il movimento nella tua app come effetto speciale senza costi. Usalo per aiutare gli utenti a capire che cosa sta facendo la tua app. Per ulteriori informazioni sulla progettazione di app con il movimento, consulta la sezione Material Design Comprendere il movimento.

Inizia

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

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

    Trendy

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

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha13")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13")
    }
    
  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 nei seguenti esempi:

    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 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 in movimento: nell'esempio MotionLayout precedente, l'attributo app:layoutDescription fa riferimento a una scena di movimento. Una scena di movimento è un file di risorse XML. All'interno dell'elemento principale <MotionScene>, una scena di movimento contiene tutte le descrizioni di movimento per il layout corrispondente. Per mantenere le informazioni di layout separate dalle descrizioni dei movimenti, ogni MotionLayout fa riferimento a una scena di movimento separata. Le definizioni nella scena di movimento hanno la precedenza su qualsiasi definizione simile in MotionLayout.

    Ecco un file di scena di movimento di esempio 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> ti consente di creare un controllo 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" significa che l'avanzamento aumenta man mano che la visualizzazione viene trascinata verso destra.

    • In <ConstraintSet> definisci i vari vincoli che descrivono il tuo 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". In orizzontale, i punti finali si trovano ai lati sinistro e destro dello schermo.

    Per un'analisi più dettagliata dei vari elementi supportati da una scena di movimento, consulta gli esempi di MotionLayout.

Attributi interpolati

All'interno di un file di scena di movimento, gli elementi ConstraintSet possono contenere attributi aggiuntivi che vengono interpolati durante la transizione. Oltre alla posizione e ai limiti, i seguenti attributi sono interpolati da MotionLayout:

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

Attributi personalizzati

All'interno di un elemento <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 elemento <CustomAttribute> contiene due attributi propri:

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

Quando specifichi un attributo personalizzato, definisci i valori dell'endpoint in entrambi gli elementi <ConstraintSet> di inizio e di fine.

Cambia il colore dello sfondo

Partendo dall'esempio precedente, supponi di voler modificare i colori della vista durante il movimento, come mostrato nella Figura 2.

Figura 2. La vista cambia il colore di sfondo durante lo spostamento.

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 visualizzare i percorsi di animazione mentre l'animazione è in esecuzione. Il valore predefinito di questo attributo è false.
  • app:progress="float" ti consente di specificare esplicitamente l'avanzamento della transizione. Puoi utilizzare qualsiasi valore con virgola mobile da 0 (l'inizio della transizione) a 1 (la fine della transizione).
  • app:currentState="reference" consente di specificare un valore ConstraintSet specifico.
  • app:motionDebug consente di visualizzare ulteriori informazioni di debug sul movimento. I valori possibili sono "SHOW_PROGRESS", "SHOW_PATH" o "SHOW_ALL".

Risorse aggiuntive

Per maggiori informazioni su MotionLayout, consulta le seguenti risorse: