Gerenciar movimento e animação de widget com o MotionLayout

MotionLayout é um tipo de layout que ajuda a gerenciar o movimento e a animação de widgets no seu app. MotionLayout é uma subclasse de ConstraintLayout e com base nos recursos recursos de layout. Como parte da biblioteca ConstraintLayout, MotionLayout está disponível como uma biblioteca de suporte.

O MotionLayout preenche a lacuna entre as transições de layout e os movimentos complexos , oferecendo uma mistura de recursos entre a animação de propriedade framework, TransitionManager e CoordinatorLayout.

Figura 1. Movimento básico controlado por toque.

Além de descrever transições entre layouts, o MotionLayout permite que você animar qualquer propriedade de layout. Além disso, ele oferece inerentemente o suporte acessível transições. Isso significa que é possível mostrar instantaneamente qualquer ponto da transição com base em alguma condição, como a entrada por toque. MotionLayout também oferece suporte frames-chave, permitindo transições totalmente personalizadas para atender às suas necessidades.

MotionLayout é totalmente declarativo, ou seja, é possível descrever qualquer transição XML, não importa a complexidade.

Considerações sobre o design

MotionLayout destina-se a mover, redimensionar e animar elementos de interface com os quais com que os usuários interagem, como botões e barras de título. Não use o movimento no seu app como um efeito especial desnecessário. Use esse recurso para ajudar os usuários a entender o que é seu app fazendo. Para mais informações sobre como projetar seu app com movimento, consulte a Seção Noções básicas sobre o Material Design movimento.

Primeiros passos

Siga estas etapas para começar a usar o MotionLayout no seu projeto.

  1. Adicione a dependência ConstraintLayout:para usar MotionLayout no seu projeto, adicione o dependência do ConstraintLayout 2.0 para a build.gradle. Se estiver usando o AndroidX, adicione o seguinte dependência:

    Groovy

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

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha14")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha14")
    }
    
  2. Crie um arquivo MotionLayout: MotionLayout como uma subclasse de ConstraintLayout, é possível transformar os ConstraintLayout existentes em um MotionLayout ao substituindo o nome da classe no arquivo de recurso de layout, conforme mostrado no exemplos a seguir:

    AndroidX

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

    Biblioteca de Suporte

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

    Aqui está um exemplo completo de um arquivo MotionLayout, que define o layout mostrado na 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>
            

    Biblioteca de Suporte

    <?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. Crie um MotionScene: no MotionLayout anterior. exemplo, o atributo app:layoutDescription faz referência a uma cena em movimento. Uma cena em movimento é um arquivo de recurso XML. Dentro de <MotionScene> elemento raiz, uma cena contém todas as descrições do movimento pelo layout correspondente. Para manter as informações de layout separadas do movimento descrições, cada MotionLayout faz referência a um movimento diferente cena As definições na cena com movimento têm precedência sobre qualquer outra definições na MotionLayout.

    Veja um exemplo de arquivo de cena em movimento que descreve a visão horizontal básica movimento na 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>
        

    Observe o seguinte:

    • <Transition> contém a definição básica do movimento.

      • motion:constraintSetStart e motion:constraintSetEnd são referências ao pontos finais do movimento. Esses endpoints são definidos <ConstraintSet> elementos mais tarde na cena em movimento.

      • motion:duration especifica o número de milissegundos. para que o movimento seja concluído.

    • <OnSwipe> permite criar um controle por toque para o movimento.

      • motion:touchAnchorId se refere à visualização que o usuário pode deslizar e arrastar.

      • motion:touchAnchorSide significa está sendo arrastada do lado direito.

      • motion:dragDirection refere-se ao progresso direção do arrasto. Por exemplo: motion:dragDirection="dragRight" significa progresso aumenta à medida que a visualização é arrastada para a direita.

    • <ConstraintSet> é onde você define as várias restrições que descrevem seu movimento. Neste exemplo, um <ConstraintSet> é definido para cada ponto final do seu movimento. Esses endpoints são centralizados verticalmente usando app:layout_constraintTop_toTopOf="parent" e app:layout_constraintBottom_toBottomOf="parent". Horizontalmente, os endpoints estão nos lados esquerdo e direito do tela.

    Para uma visão mais detalhada dos vários elementos que uma cena em movimento suporta, consulte a Exemplos do MotionLayout.

Atributos interpolados

Dentro de um arquivo de cena em movimento, os elementos ConstraintSet podem conter atributos interpolados durante a transição. Além da posição e limites, os seguintes atributos são interpolados por MotionLayout:

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

Atributos personalizados

Em um <Constraint>, você pode usar o elemento <CustomAttribute> para especificar uma transição para atributos que não são simplesmente relacionados à posição ou View atributos.

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

Um <CustomAttribute> contém dois atributos próprios:

  • motion:attributeName é obrigatório e deve corresponder a um objeto com getter e setter. O getter e o setter precisam corresponder a um padrão específico. Para exemplo, há suporte para backgroundColor, já que a visualização tem getBackgroundColor() e setBackgroundColor().
  • O outro atributo que você precisa fornecer é baseado no tipo de valor. Escolha entre os seguintes tipos compatíveis:
    • motion:customColorValue para cores
    • motion:customIntegerValue para números inteiros
    • motion:customFloatValue para flutuantes
    • motion:customStringValue para strings
    • motion:customDimension para dimensões
    • motion:customBoolean para booleanos

Ao especificar um atributo personalizado, defina valores de ponto de extremidade no início e elementos <ConstraintSet> finais.

Mudar cor do plano de fundo

Com base no exemplo anterior, suponha que você queira que as cores da visualização mudem como parte do movimento, como mostrado na figura 2.

Figura 2. A visualização muda a cor de fundo à medida que se move.

Adicione um elemento <CustomAttribute> a cada elemento ConstraintSet, conforme mostrado em o seguinte snippet de código:

<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>

Atributos adicionais do MotionLayout

Além dos atributos no exemplo anterior, MotionLayout tem outros atributos que convém especificar:

  • app:applyMotionScene="boolean" indica se a cena em movimento será aplicada. O valor padrão para este atributo é true.
  • app:showPaths="boolean" indica se as demarcações de animação serão mostradas como movimento está correndo. O valor padrão para este atributo é false.
  • app:progress="float": permite que você especifique explicitamente o andamento da transição. Você pode usar qualquer valor de ponto flutuante de 0 (o início da transição) até 1. (o fim da transição).
  • app:currentState="reference": permite que você especifique um ConstraintSet.
  • app:motionDebug permite exibir mais informações de depuração sobre o movimento. Os valores possíveis são "SHOW_PROGRESS", "SHOW_PATH" ou "SHOW_ALL".

Outros recursos

Para mais informações sobre MotionLayout, consulte os seguintes recursos: