1. Antes de começar
Este codelab faz parte do curso Android avançado no Kotlin. Você vai aproveitar mais este curso se fizer os codelabs em sequência, mas isso não é obrigatório. Todos os codelabs do curso estão listados na página de destino dos codelabs do Android avançado em Kotlin.
O MotionLayout é uma biblioteca que permite adicionar movimentos avançados ao seu app Android. Ela é baseada no ConstraintLayout, e permite animar qualquer coisa que você possa criar usando o ConstraintLayout.
É possível usar o MotionLayout para animar o local, o tamanho, a visibilidade, o alfa, a cor, a elevação, a rotação e outros atributos de várias visualizações ao mesmo tempo. Com o XML declarativo, é possível criar animações coordenadas que envolvem várias visualizações difíceis de atingir no código.
As animações são uma ótima maneira de melhorar a experiência de um app. Você pode usar animações para:
- Mostrar mudanças: a animação entre estados permite que o usuário acompanhe naturalmente as mudanças na sua interface.
- Chame a atenção: use animações para destacar elementos importantes da interface.
- Crie designs bonitos: movimentos eficazes no design deixam os apps com uma aparência refinada.
Pré-requisitos
Este codelab foi projetado para desenvolvedores com alguma experiência em desenvolvimento para Android. Antes de tentar concluir este codelab, você precisa:
- Saber como criar um app com uma atividade, um layout básico e executá-lo em um dispositivo ou emulador usando o Android Studio. Conheça o
ConstraintLayout. Leia o codelab de layout restrito para saber mais sobreConstraintLayout.
O que você aprenderá
- Definir uma animação com
ConstraintSetseMotionLayout - Animar com base em eventos de arrastar
- Mudar a animação com
KeyPosition - Mudar atributos com
KeyAttribute - Executar animações com código
- Animar cabeçalhos recolhíveis com
MotionLayout
O que é necessário
- Android Studio 4.0. O editor
MotionLayoutsó funciona com essa versão do Android Studio.
2. Primeiros passos
Para fazer o download do app de exemplo, você pode:
… ou clone o repositório do GitHub pela linha de comando usando o seguinte comando:
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. Criar animações com o MotionLayout
Primeiro, você vai criar uma animação que move uma visualização do início da parte de cima da tela até a parte de baixo em resposta aos cliques do usuário.
Para criar uma animação com base no código inicial, você precisa dos seguintes elementos principais:
- Um
MotionLayout,que é uma subclasse deConstraintLayout. Você especifica todas as visualizações a serem animadas na tagMotionLayout. - Um
MotionScene,, que é um arquivo XML que descreve uma animação paraMotionLayout.. - Um
Transition,que faz parte doMotionSceneque especifica a duração, o gatilho e como mover as visualizações da animação. - Um
ConstraintSetque especifica as restrições de início e fim da transição.
Vamos analisar cada um deles, começando pelo MotionLayout.
Etapa 1: analisar o código atual
MotionLayout é uma subclasse de ConstraintLayout, então ele é compatível com todos os mesmos recursos ao adicionar animação. Para usar MotionLayout, adicione uma visualização MotionLayout onde você usaria ConstraintLayout..
- Em
res/layout, abraactivity_step1.xml.. Aqui você tem umConstraintLayoutcom um únicoImageViewde uma estrela, com uma tonalidade aplicada dentro dela.
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>
Esse ConstraintLayout não tem restrições. Portanto, se você executasse o app agora, veria a estrela sem restrições, o que significa que ela seria posicionada em um local desconhecido. O Android Studio vai mostrar um aviso sobre a falta de restrições.
Etapa 2: converter para Motion Layout
Para animar usando MotionLayout,, é necessário converter o ConstraintLayout em um MotionLayout.
Para que seu layout use uma cena de movimento, ele precisa apontar para ela.
- Para fazer isso, abra a superfície de design. No Android Studio 4.0, abra a superfície de design usando o ícone de divisão ou design no canto superior direito ao analisar um arquivo XML de layout.

- Depois de abrir a superfície de design, clique com o botão direito do mouse na prévia e selecione Converter em MotionLayout.

Isso substitui a tag ConstraintLayout por uma tag MotionLayout e adiciona um motion:layoutDescription à tag MotionLayout que aponta para @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">
Uma cena de movimento é um único arquivo XML que descreve uma animação em um MotionLayout.
Assim que você fizer a conversão para um MotionLayout, a superfície de design vai mostrar o Motion Editor.

Há três novos elementos de interface no Motion Editor:
- Visão geral: é uma seleção modal que permite escolher diferentes partes da animação. Nesta imagem, o
startConstraintSetestá selecionado. Você também pode selecionar a transição entrestarteendclicando na seta entre eles. - Seção: abaixo da visão geral, há uma janela de seção que muda de acordo com o item selecionado. Nesta imagem, as informações
startConstraintSetsão mostradas na janela de seleção. - Atributo: o painel de atributos mostra e permite editar os atributos do item selecionado na visão geral ou na janela de seleção. Nesta imagem, os atributos do
startConstraintSetestão sendo mostrados.
Etapa 3: definir restrições de início e fim
Todas as animações podem ser definidas em termos de um início e um fim. O início descreve a aparência da tela antes da animação, e o fim descreve a aparência da tela após a conclusão da animação. A MotionLayout é responsável por descobrir como animar entre o estado inicial e final (ao longo do tempo).
O MotionScene usa uma tag ConstraintSet para definir os estados inicial e final. Um ConstraintSet é exatamente o que parece: um conjunto de restrições que podem ser aplicadas a visualizações. Isso inclui restrições de largura, altura e ConstraintLayout. Ele também inclui alguns atributos, como alpha. Ele não contém as visualizações em si, apenas as restrições delas.
As restrições especificadas em um ConstraintSet substituem as restrições especificadas no arquivo de layout. Se você definir restrições no layout e no MotionScene, apenas as restrições no MotionScene serão aplicadas.
Nesta etapa, você vai restringir a visualização da estrela para que ela comece na parte superior da tela e termine na parte inferior.
Você pode concluir esta etapa usando o Motion Editor ou editando o texto de activity_step1_scene.xml diretamente.
- Selecione o
startConstraintSet no painel de visão geral.

- No painel seleção, escolha
red_star. No momento, ele mostra "Origem delayout", o que significa que não está restrito a esteConstraintSet. Use o ícone de lápis no canto superior direito para Criar restrição.

- Confirme se
red_starmostra uma origem destartquando ostartConstraintSeté selecionado no painel de visão geral. - No painel "Atributos", com
red_starselecionado nostartConstraintSet, adicione uma restrição na parte de cima e comece clicando nos botões azuis +.

- Abra
xml/activity_step1_scene.xmlpara conferir o código que o Motion Editor gerou para essa restrição.
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>
O ConstraintSet tem um id de @id/start e especifica todas as restrições a serem aplicadas a todas as visualizações no MotionLayout. Como esse MotionLayout tem apenas uma vista, ele só precisa de um Constraint.
O Constraint dentro do ConstraintSet especifica o ID da visualização que está sendo restringida, @id/red_star definido em activity_step1.xml. É importante observar que as tags Constraint especificam apenas restrições e informações de layout. A tag Constraint não sabe que está sendo aplicada a um ImageView.
Essa restrição especifica a altura, a largura e as outras duas restrições necessárias para restringir a visualização red_star à parte superior inicial do elemento pai.
- Selecione o
endConstraintSet no painel de visão geral.

- Siga as mesmas etapas de antes para adicionar um
Constraintparared_starnoendConstraintSet. - Para usar o Motion Editor e concluir esta etapa, adicione uma restrição a
bottomeendclicando nos botões azuis +.

- O código em XML é semelhante a este:
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>
Assim como @id/start, esse ConstraintSet tem um único Constraint em @id/red_star. Desta vez, ele restringe a parte de baixo da tela.
Não é necessário nomeá-los como @id/start e @id/end, mas é conveniente fazer isso.
Etapa 4: definir uma transição
Cada MotionScene também precisa incluir pelo menos uma transição. Uma transição define todas as partes de uma animação, do início ao fim.
Uma transição precisa especificar um ConstraintSet de início e de fim. Uma transição também pode especificar como modificar a animação de outras maneiras, como por quanto tempo executar a animação ou como animar arrastando visualizações.
- O Motion Editor criou uma transição para nós por padrão ao criar o arquivo MotionScene. Abra
activity_step1_scene.xmlpara ver a transição gerada.
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>
Isso é tudo o que o MotionLayout precisa para criar uma animação. Analisando cada atributo:
- O
constraintSetStartserá aplicado às visualizações quando a animação começar. constraintSetEndserá aplicado às visualizações no final da animação.durationespecifica quanto tempo a animação vai durar em milissegundos.
Em seguida, o MotionLayout vai descobrir um caminho entre as restrições de início e fim e animá-lo pela duração especificada.
Etapa 5: visualizar a animação no Motion Editor

Animação:vídeo mostrando uma prévia de transição no Motion Editor
- Abra o Motion Editor e selecione a transição clicando na seta entre
starteendno painel de visão geral.

- O painel Seleção mostra controles de reprodução e uma barra de limpeza quando uma transição é selecionada. Clique em "Reproduzir" ou arraste a posição atual para conferir uma prévia da animação.

Etapa 6: adicionar um manipulador de cliques
Você precisa de uma maneira de iniciar a animação. Uma maneira de fazer isso é fazer com que o MotionLayout responda a eventos de clique em @id/red_star.
- Abra o editor de movimento e selecione a transição clicando na seta entre o início e o fim no painel de visão geral.

- Clique em
Criar manipulador de clique ou deslize na barra de ferramentas do painel de visão geral . Isso adiciona um manipulador que vai iniciar uma transição. - Selecione Manipulador de cliques no pop-up.

- Mude a Visualização para clique para
red_star.

- Clique em Adicionar. O manipulador de cliques é representado por um pequeno ponto na transição no Motion Editor.

- Com a transição selecionada no painel de visão geral, adicione um atributo
clickActiondetoggleao gerenciador OnClick que você acabou de adicionar no painel de atributos.

- Abra
activity_step1_scene.xmlpara ver o código gerado pelo 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>
O Transition informa ao MotionLayout para executar a animação em resposta a eventos de clique usando uma tag <OnClick>. Analisando cada atributo:
targetIdé a visualização a ser observada para cliques.clickActiondetogglevai alternar entre o estado inicial e final ao clicar. Confira outras opções paraclickActionna documentação.
- Execute o código, clique em Etapa 1 e na estrela vermelha para ver a animação.
Etapa 5: animações em ação
Execute o app. A animação será executada quando você clicar na estrela.

O arquivo de cena em movimento concluído define um Transition que aponta para um ConstraintSet de início e fim.
No início da animação (@id/start), o ícone de estrela fica restrito à parte superior da tela. No final da animação (@id/end), o ícone de estrela é restrito à parte de baixo da tela.
<?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. Animação com base em eventos de arrastar
Nesta etapa, você vai criar uma animação que responde a um evento de arrastar do usuário (quando ele desliza a tela) para executar a animação. A MotionLayout oferece suporte ao rastreamento de eventos de toque para mover visualizações, bem como gestos de rolagem rápida baseados em física para tornar o movimento fluido.
Etapa 1: inspecionar o código inicial
- Para começar, abra o arquivo de layout
activity_step2.xml, que tem umMotionLayout. Confira o código.
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>
Esse layout define todas as visualizações da animação. Os três ícones de estrela não são restritos no layout porque serão animados na cena de movimento.
Os créditos TextView têm restrições aplicadas porque permanecem no mesmo lugar durante toda a animação e não modificam nenhum atributo.
Etapa 2: animar a cena
Assim como a última animação, ela será definida por um ConstraintSet, inicial e final e um Transition.
Defina o ConstraintSet inicial.
- Abra a cena em movimento
xml/step2.xmlpara definir a animação. - Adicione as restrições para a restrição inicial
start. No início, todas as três estrelas ficam centralizadas na parte de baixo da tela. As estrelas da direita e da esquerda têm um valoralphade0.0, o que significa que elas são totalmente transparentes e estão ocultas.
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>
Neste ConstraintSet, especifique um Constraint para cada uma das estrelas. Cada restrição será aplicada por MotionLayout no início da animação.
Cada visualização de estrela é centralizada na parte de baixo da tela usando restrições de início, fim e parte de baixo. As duas estrelas @id/left_star e @id/right_star têm um valor alfa adicional que as torna invisíveis e que será aplicado no início da animação.
Os conjuntos de restrições start e end definem o início e o fim da animação. Uma restrição no início, como motion:layout_constraintStart_toStartOf, restringe o início de uma visualização ao início de outra. Isso pode ser confuso no início, porque o nome start é usado para e, e ambos são usados no contexto de restrições. Para ajudar a distinguir, o start em layout_constraintStart se refere ao "início" da visualização, que é a esquerda em um idioma da esquerda para a direita e a direita em um idioma da direita para a esquerda. O conjunto de restrições start se refere ao início da animação.
Definir o ConstraintSet final
- Defina a restrição de fim para usar uma corrente e posicionar as três estrelas juntas abaixo de
@id/credits. Além disso, ele vai definir o valor final doalphadas estrelas esquerda e direita como1.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>
O resultado final é que as visualizações vão se espalhar para cima e para fora do centro à medida que são animadas.
Além disso, como a propriedade alpha é definida em @id/right_start e @id/left_star nos dois ConstraintSets, as duas visualizações vão aparecer gradualmente à medida que a animação avança.
Animação com base no deslizar do usuário
O MotionLayout pode rastrear eventos de arrastar do usuário ou um deslizar para criar uma animação de "movimento rápido" baseada em física. Isso significa que as visualizações vão continuar se o usuário rolar rapidamente e vão diminuir a velocidade como um objeto físico faria ao rolar em uma superfície. É possível adicionar esse tipo de animação com uma tag OnSwipe no Transition.
- Substitua o TODO para adicionar uma tag
OnSwipepor<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 contém alguns atributos, sendo o mais importante touchAnchorId.
touchAnchorIdé a visualização rastreada que se move em resposta ao toque. OMotionLayoutvai manter essa visualização na mesma distância do dedo que está deslizando.touchAnchorSidedetermina qual lado da visualização deve ser rastreado. Isso é importante para visualizações que são redimensionadas, seguem caminhos complexos ou têm um lado que se move mais rápido que o outro.dragDirectiondetermina qual direção é importante para essa animação (para cima, para baixo, para a esquerda ou para a direita).
Quando MotionLayout detecta eventos de arrastar, o listener é registrado na visualização MotionLayout e não na visualização especificada por touchAnchorId. Quando um usuário inicia um gesto em qualquer lugar da tela, o MotionLayout mantém constante a distância entre o dedo e o touchAnchorSide da visualização touchAnchorId. Se eles tocarem a 100 dp de distância do lado da âncora, por exemplo, MotionLayout vai manter esse lado a 100 dp de distância do dedo durante toda a animação.
Faça um teste
- Execute o app novamente e abra a tela da etapa 2. A animação vai aparecer.
- Tente "jogar" ou soltar o dedo na metade da animação para conferir como o
MotionLayoutmostra animações fluidas baseadas em física.

O MotionLayout pode animar entre designs muito diferentes usando os recursos do ConstraintLayout para criar efeitos avançados.
Nesta animação, todas as três visualizações são posicionadas em relação ao elemento pai na parte de baixo da tela para começar. No final, as três visualizações são posicionadas em relação a @id/credits em uma cadeia.
Apesar desses layouts muito diferentes, MotionLayout vai criar uma animação fluida entre o início e o fim.
5. Como modificar um caminho
Nesta etapa, você vai criar uma animação que segue um caminho complexo durante a animação e anima os créditos durante o movimento. O MotionLayout pode modificar o caminho que uma visualização vai percorrer entre o início e o fim usando um KeyPosition.
Etapa 1: analisar o código atual
- Abra
layout/activity_step3.xmlexml/step3.xmlpara conferir o layout e a cena de movimento atuais. UmImageViewe umTextViewmostram a lua e o texto de créditos. - Abra o arquivo de cena em movimento (
xml/step3.xml). Você vai ver que umaTransitionde@id/startpara@id/endestá definida. A animação move a imagem da lua do canto inferior esquerdo para o canto inferior direito da tela usando doisConstraintSets. O texto dos créditos aparece gradualmente dealpha="0.0"paraalpha="1.0"à medida que a lua se move. - Execute o app e selecione Etapa 3. Você vai notar que a lua segue um caminho linear (ou uma linha reta) do início ao fim quando você clica nela.
Etapa 2: ativar a depuração de caminhos
Antes de adicionar um arco ao movimento da lua, é útil ativar a depuração de caminhos em MotionLayout.
Para ajudar a desenvolver animações complexas com o MotionLayout, é possível desenhar o caminho da animação de cada visualização. Isso é útil quando você quer visualizar a animação e ajustar os pequenos detalhes do movimento.
- Para ativar os caminhos de depuração, abra
layout/activity_step3.xmle adicionemotion:motionDebug="SHOW_PATH"à tagMotionLayout.
activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
Depois de ativar a depuração de caminhos, quando você executar o app novamente, vai ver os caminhos de todas as visualizações com uma linha pontilhada.

- Os círculos representam a posição inicial ou final de uma visualização.
- As linhas representam o caminho de uma visualização.
- Os diamantes representam um
KeyPositionque modifica o caminho.
Por exemplo, nesta animação, o círculo do meio é a posição do texto dos créditos.
Etapa 3: modificar um caminho
Todas as animações em MotionLayout são definidas por um início e um fim ConstraintSet, que definem a aparência da tela antes e depois da animação. Por padrão, MotionLayout cria um caminho linear (uma linha reta) entre as posições inicial e final de cada visualização que muda de posição.
Para criar caminhos complexos, como o arco da lua neste exemplo, o MotionLayout usa um KeyPosition para modificar o caminho que uma visualização faz entre o início e o fim.
- Abra
xml/step3.xmle adicione umKeyPositionà cena. A tagKeyPositioné colocada dentro da 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>
Um KeyFrameSet é filho de um Transition e é um conjunto de todos os KeyFrames, como KeyPosition, que devem ser aplicados durante a transição.
À medida que MotionLayout calcula o caminho da lua entre o início e o fim, ele modifica o caminho com base no KeyPosition especificado no KeyFrameSet. Para ver como isso modifica o caminho, execute o app novamente.
Uma KeyPosition tem vários atributos que descrevem como ela modifica o caminho. Os mais importantes são:
framePositioné um número entre 0 e 100. Ele define quando na animação esseKeyPositiondeve ser aplicado, sendo 1 1% da animação e 99 99% da animação. Então, se o valor for 50, aplique-o bem no meio.motionTargeté a visualização para a qualKeyPositionmodifica o caminho.keyPositionTypeé como esseKeyPositionmodifica o caminho. Pode serparentRelative,pathRelativeoudeltaRelative(conforme explicado na próxima etapa).percentX | percentYé o quanto modificar o caminho emframePosition(valores entre 0,0 e 1,0, com valores negativos e valores >1 permitidos).
Pense assim: "Em framePosition, modifique o caminho de motionTarget movendo-o em percentX ou percentY de acordo com as coordenadas determinadas por keyPositionType".
Por padrão, MotionLayout arredonda todos os cantos introduzidos pela modificação do caminho. Se você observar a animação que acabou de criar, vai notar que a lua segue um caminho curvo na curva. Para a maioria das animações, é isso que você quer. Caso contrário, especifique o atributo curveFit para personalizar.
Fazer um teste
Se você executar o app novamente, vai ver a animação dessa etapa.

A lua segue um arco porque passa por um KeyPosition especificado no Transition.
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
Você pode ler este KeyPosition como: "Em framePosition 50 (metade da animação), modifique o caminho de motionTarget @id/moon movendo-o em 50% Y (metade da tela) de acordo com as coordenadas determinadas por parentRelative (todo o MotionLayout)".
Assim, na metade da animação, a lua precisa passar por um KeyPosition que está 50% abaixo na tela. Esse KeyPosition não modifica o movimento X, então a lua ainda vai do início ao fim na horizontal. O MotionLayout vai descobrir um caminho suave que passa por esse KeyPosition enquanto se move entre o início e o fim.
Se você olhar de perto, vai notar que o texto dos créditos é limitado pela posição da lua. Por que ele não se move verticalmente também?

<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
Acontece que, mesmo modificando o caminho da lua, as posições inicial e final dela não a movem verticalmente. O KeyPosition não modifica a posição inicial ou final. Portanto, o texto dos créditos é restrito à posição final da lua.
Se você quiser que os créditos se movam com a lua, adicione um KeyPosition a eles ou modifique as restrições de início em @id/credits.
Na próxima seção, você vai conhecer os diferentes tipos de keyPositionType em MotionLayout.
6. Noções básicas sobre keyPositionType
Na última etapa, você usou um tipo keyPosition de parentRelative para compensar o caminho em 50% da tela. O atributo keyPositionType determina como o MotionLayout vai modificar o caminho de acordo com percentX ou percentY.
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Há três tipos diferentes de keyPosition possíveis: parentRelative, pathRelative e deltaRelative. Especificar um tipo muda o sistema de coordenadas pelo qual percentX e percentY são calculados.
O que é um sistema de coordenadas?
Um sistema de coordenadas oferece uma maneira de especificar um ponto no espaço. Eles também são úteis para descrever uma posição na tela.
Os sistemas de coordenadas MotionLayout são um sistema de coordenadas cartesianas. Isso significa que eles têm um eixo X e um eixo Y definidos por duas linhas perpendiculares. A principal diferença entre eles é onde o eixo X fica na tela (o eixo Y é sempre perpendicular ao eixo X).
Todos os sistemas de coordenadas em MotionLayout usam valores entre 0.0 e 1.0 nos eixos X e Y. Eles permitem valores negativos e maiores que 1.0. Por exemplo, um valor percentX de -2.0 significa ir na direção oposta do eixo X duas vezes.
Se tudo isso parecer muito com uma aula de álgebra, confira as imagens abaixo.
Coordenadas parentRelative

O keyPositionType de parentRelative usa o mesmo sistema de coordenadas da tela. Ele define (0, 0) como o canto superior esquerdo de todo o MotionLayout e (1, 1) como o canto inferior direito.
Use parentRelative sempre que quiser fazer uma animação que se mova por todo o MotionLayout, como o arco da lua neste exemplo.
No entanto, se você quiser modificar um caminho em relação ao movimento, por exemplo, fazer uma pequena curva, os outros dois sistemas de coordenadas serão uma opção melhor.
coordenadas deltaRelative

Delta é um termo matemático para mudança. Portanto, deltaRelative é uma maneira de dizer "mudança relativa". Nas coordenadas deltaRelative, (0,0) é a posição inicial da visualização, e (1,1) é a posição final. Os eixos X e Y estão alinhados com a tela.
O eixo X é sempre horizontal na tela, e o eixo Y é sempre vertical. Em comparação com parentRelative, a principal diferença é que as coordenadas descrevem apenas a parte da tela em que a visualização vai se mover.
deltaRelative é um ótimo sistema de coordenadas para controlar o movimento horizontal ou vertical isoladamente. Por exemplo, é possível criar uma animação que conclui apenas o movimento vertical (Y) em 50% e continua animando na horizontal (X).
pathRelative coordinates

O último sistema de coordenadas em MotionLayout é pathRelative. Ele é bem diferente dos outros dois, já que o eixo X segue a trajetória de animação do início ao fim. Portanto, (0,0) é a posição inicial, e (1,0) é a posição final.
Por que você faria isso? Isso é surpreendente à primeira vista, principalmente porque esse sistema de coordenadas não está alinhado ao sistema de coordenadas da tela.
Acontece que pathRelative é muito útil para algumas coisas.
- Acelerar, desacelerar ou parar uma visualização durante parte da animação. Como a dimensão X sempre corresponde exatamente ao caminho percorrido pela visualização, é possível usar um
pathRelativeKeyPositionpara mudar qualframePositionum ponto específico nesse caminho é alcançado. Assim, umKeyPositionemframePosition="50"com umpercentX="0.1"faria com que a animação levasse 50% do tempo para percorrer os primeiros 10% do movimento. - Adicionar um arco sutil a um caminho. Como a dimensão Y é sempre perpendicular ao movimento, mudar Y vai alterar o caminho para curvar em relação ao movimento geral.
- Não é possível adicionar uma segunda dimensão quando
deltaRelativenão funciona. Para movimentos completamente horizontais e verticais,deltaRelativecria apenas uma dimensão útil. No entanto,pathRelativesempre vai criar coordenadas X e Y utilizáveis.
Na próxima etapa, você vai aprender a criar caminhos ainda mais complexos usando mais de um KeyPosition.
7. Como criar caminhos complexos
Analisando a animação criada na última etapa, ela cria uma curva suave, mas o formato poderia ser mais parecido com uma lua.
Modificar um caminho com vários elementos KeyPosition
MotionLayout pode modificar ainda mais um caminho definindo quantos KeyPosition forem necessários para obter qualquer movimento. Para esta animação, você vai criar um arco, mas pode fazer a lua pular para cima e para baixo no meio da tela, se quiser.
- Abra
xml/step4.xml. Ele tem as mesmas visualizações e oKeyFrameque você adicionou na etapa anterior. - Para completar a parte de cima da curva, adicione mais dois
KeyPositionsao caminho de@id/moon, um pouco antes de chegar ao topo e outro depois.

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"
/>
Esses KeyPositions serão aplicados 25% e 75% do caminho da animação e farão com que @id/moon se mova por um caminho que fica a 60% da parte de cima da tela. Combinado com o KeyPosition atual em 50%, isso cria um arco suave para a lua seguir.
No MotionLayout, adicione quantos KeyPositions forem necessários para criar a trajetória de animação desejada. O MotionLayout vai aplicar cada KeyPosition no framePosition especificado e descobrir como criar um movimento suave que passe por todos os KeyPositions.
Fazer um teste
- Execute o app novamente. Acesse a Etapa 4 para ver a animação em ação. Quando você clica na lua, ela segue o caminho do início ao fim, passando por cada
KeyPositionespecificado noKeyFrameSet.
Explore por conta própria
Antes de passar para outros tipos de KeyFrame, tente adicionar mais KeyPositions ao KeyFrameSet para ver que tipo de efeitos você pode criar usando apenas KeyPosition.
Confira um exemplo de como criar um caminho complexo que se move para frente e para trás durante a animação.

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>
Depois de explorar KeyPosition, na próxima etapa você vai conhecer outros tipos de KeyFrames.
8. Mudança de atributos durante o movimento
Criar animações dinâmicas geralmente significa mudar o size, rotation ou alpha das visualizações à medida que a animação avança. O MotionLayout permite animar vários atributos em qualquer visualização usando um KeyAttribute.
Nesta etapa, você vai usar KeyAttribute para dimensionar e girar a lua. Você também vai usar um KeyAttribute para atrasar o aparecimento do texto até que a lua quase complete a jornada.
Etapa 1: redimensionar e girar com KeyAttribute
- Abra
xml/step5.xml, que contém a mesma animação criada na etapa anterior. Para variar, essa tela usa uma imagem diferente do espaço como plano de fundo. - Para fazer a lua aumentar de tamanho e girar, adicione duas tags
KeyAttributenoKeyFrameSetemkeyFrame="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"
/>
Esses KeyAttributes são aplicados em 50% e 100% da animação. O primeiro KeyAttribute a 50% vai acontecer na parte de cima do arco, fazendo com que a visualização dobre de tamanho e gire -360 graus (ou um círculo completo). O segundo KeyAttribute vai concluir a segunda rotação para -720 graus (dois círculos completos) e reduzir o tamanho de volta ao normal, já que os valores scaleX e scaleY são definidos como 1,0 por padrão.
Assim como um KeyPosition, um KeyAttribute usa framePosition e motionTarget para especificar quando aplicar o KeyFrame e qual visualização modificar. O MotionLayout vai fazer a interpolação entre KeyPositions para criar animações fluidas.
O KeyAttributes oferece suporte a atributos que podem ser aplicados a todas as visualizações. Eles permitem mudar atributos básicos, como visibility, alpha ou elevation. Você também pode mudar a rotação como está fazendo aqui, girar em três dimensões com rotateX e rotateY, dimensionar o tamanho com scaleX e scaleY ou traduzir a posição da visualização em X, Y ou Z.
Etapa 2: atrasar o aparecimento dos créditos
Uma das metas desta etapa é atualizar a animação para que o texto dos créditos não apareça até que a animação esteja quase concluída.
- Para atrasar o aparecimento dos créditos, defina mais um
KeyAttributeque garanta quealphapermaneça 0 atékeyPosition="85".MotionLayoutainda vai fazer uma transição suave de 0 para 100 alfa, mas isso vai acontecer nos últimos 15% da animação.
step5.xml
<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->
<KeyAttribute
motion:framePosition="85"
motion:motionTarget="@id/credits"
android:alpha="0.0"
/>
Esse KeyAttribute mantém o alpha de @id/credits em 0,0 nos primeiros 85% da animação. Como ele começa com um alfa de 0, isso significa que ele vai ficar invisível nos primeiros 85% da animação.
O efeito final desse KeyAttribute é que os créditos aparecem no final da animação. Isso dá a impressão de que eles estão coordenados com a Lua, que se acomoda no canto direito da tela.
Ao atrasar as animações em uma visualização enquanto outra se move assim, você pode criar animações impressionantes que parecem dinâmicas para o usuário.
Fazer um teste
- Execute o app novamente e vá para a Etapa 5 para ver a animação em ação. Quando você clicar na lua, ela vai seguir o caminho do início ao fim, passando por cada
KeyAttributeespecificado noKeyFrameSet.

Como você gira a lua dois círculos completos, ela vai dar um salto duplo para trás, e os créditos vão atrasar a aparição até que a animação esteja quase concluída.
Explore por conta própria
Antes de passar para o tipo final de KeyFrame, tente modificar outros atributos padrão no KeyAttributes. Por exemplo, tente mudar rotation para rotationX para ver qual animação ele produz.
Confira uma lista dos atributos padrão que você pode testar:
android:visibilityandroid:alphaandroid:elevationandroid:rotationandroid:rotationXandroid:rotationYandroid:scaleXandroid:scaleYandroid:translationXandroid:translationYandroid:translationZ
9. Mudar atributos personalizados
Animações avançadas envolvem a mudança da cor ou de outros atributos de uma visualização. Enquanto MotionLayout pode usar um KeyAttribute para mudar qualquer um dos atributos padrão listados na tarefa anterior, você usa um CustomAttribute para especificar qualquer outro atributo.
Um CustomAttribute pode ser usado para definir qualquer valor que tenha um setter. Por exemplo, é possível definir o backgroundColor em uma View usando um CustomAttribute. O MotionLayout usa reflexão para encontrar o setter e o chama repetidamente para animar a visualização.
Nesta etapa, você vai usar um CustomAttribute para definir o atributo colorFilter na lua e criar a animação mostrada abaixo.

Definir atributos personalizados
- Para começar, abra
xml/step6.xml, que contém a mesma animação criada na etapa anterior. - Para fazer a lua mudar de cor, adicione dois
KeyAttributecom umCustomAttributenoKeyFrameSetemkeyFrame="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>
Você adiciona um CustomAttribute dentro de um KeyAttribute. O CustomAttribute será aplicado no framePosition especificado pelo KeyAttribute.
Dentro do CustomAttribute, especifique um attributeName e um valor a ser definido.
motion:attributeNameé o nome do setter que será chamado por esse atributo personalizado. Neste exemplo,setColorFilteremDrawableserá chamado.motion:custom*Valueé um valor personalizado do tipo indicado no nome. Neste exemplo, o valor personalizado é uma cor especificada.
Os valores personalizados podem ter qualquer um dos seguintes tipos:
- Cor
- Número inteiro
- Ponto flutuante
- String
- Dimensão
- Booleano
Com essa API, o MotionLayout pode animar qualquer coisa que forneça um setter em qualquer visualização.
Fazer um teste
- Execute o app novamente e acesse Etapa 6 para conferir a animação em ação. Quando você clicar na lua, ela vai seguir o caminho do início ao fim, passando por cada
KeyAttributeespecificado noKeyFrameSet.

Quando você adiciona mais KeyFrames, o MotionLayout muda o caminho da lua de uma linha reta para uma curva complexa, adicionando um salto duplo para trás, redimensionamento e uma mudança de cor no meio da animação.
Em animações reais, geralmente você anima várias visualizações ao mesmo tempo, controlando o movimento delas em diferentes caminhos e velocidades. Ao especificar um KeyFrame diferente para cada visualização, é possível coreografar animações avançadas que animam várias visualizações com MotionLayout.
10. Eventos de arrastar e caminhos complexos
Nesta etapa, você vai aprender a usar OnSwipe com caminhos complexos. Até agora, a animação da lua foi acionada por um listener OnClick e é executada por uma duração fixa.
Para controlar animações com trajetórias complexas usando OnSwipe, como a animação da lua que você criou nas últimas etapas, é necessário entender como OnSwipe funciona.
Etapa 1: analisar o comportamento do OnSwipe
- Abra
xml/step7.xmle encontre a declaraçãoOnSwipe.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- Execute o app no seu dispositivo e acesse a Etapa 7. Arraste a lua ao longo do caminho do arco para criar uma animação suave.
Quando você executa essa animação, ela não fica muito boa. Depois que a lua atinge o topo do arco, ela começa a pular.

Para entender o bug, considere o que acontece quando o usuário toca logo abaixo da parte de cima do arco. Como a tag OnSwipe tem um motion:touchAnchorSide="bottom", o MotionLayout tentará manter constante a distância entre o dedo e a parte de baixo da visualização durante toda a animação.
Mas, como a parte de baixo da lua nem sempre vai na mesma direção, ela sobe e depois desce, o MotionLayout não sabe o que fazer quando o usuário acabou de passar pelo topo do arco. Para considerar isso, já que você está rastreando a parte de baixo da lua, onde ela deve ser colocada quando o usuário tocar aqui?

Etapa 2: use o lado direito
Para evitar bugs como esse, é importante sempre escolher um touchAnchorId e um touchAnchorSide que sempre avancem em uma direção durante toda a animação.
Nesta animação, o lado right e o lado left da lua vão avançar pela tela em uma direção.
No entanto, bottom e top vão inverter a direção. Quando o OnSwipe tenta rastreá-los, ele fica confuso quando a direção muda.
- Para fazer com que a animação siga os eventos de toque, mude
touchAnchorSidepararight.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
Etapa 3: usar dragDirection
Também é possível combinar dragDirection com touchAnchorSide para fazer com que uma faixa secundária siga uma direção diferente do normal. Ainda é importante que o touchAnchorSide avance apenas em uma direção, mas você pode informar ao MotionLayout qual direção rastrear. Por exemplo, você pode manter o touchAnchorSide="bottom", mas adicionar dragDirection="dragRight". Isso fará com que MotionLayout rastreie a posição da parte de baixo da visualização, mas só considere a localização dela ao mover para a direita (ignorando o movimento vertical). Assim, mesmo que a parte de baixo suba e desça, ela ainda será animada corretamente com OnSwipe.
- Atualize
OnSwipepara rastrear o movimento da Lua corretamente.
step7.xml
<!-- Using dragDirection to control the direction of drag tracking →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragRight"
/>
Fazer um teste
- Execute o app novamente e tente arrastar a lua por todo o caminho. Mesmo seguindo um arco complexo, o
MotionLayoutpoderá progredir na animação em resposta a eventos de deslizar.

11. Executar movimento com código
MotionLayout pode ser usado para criar animações avançadas com CoordinatorLayout. Nesta etapa, você vai criar um cabeçalho recolhível usando MotionLayout.
Etapa 1: analisar o código atual
- Para começar, abra
layout/activity_step8.xml. - Em
layout/activity_step8.xml, você vê que umCoordinatorLayoute umAppBarLayoutfuncionais já estão criados.
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>
Esse layout usa um CoordinatorLayout para compartilhar informações de rolagem entre o NestedScrollView e o AppBarLayout. Assim, quando o NestedScrollView rolar para cima, ele vai informar o AppBarLayout sobre a mudança. É assim que você implementa uma barra de ferramentas recolhível como esta no Android: a rolagem do texto é "coordenada" com o cabeçalho recolhível.
A cena de movimento apontada por @id/motion_layout é semelhante à cena de movimento da última etapa. No entanto, a declaração OnSwipe foi removida para permitir que ela funcione com CoordinatorLayout.
- Execute o app e vá para a Etapa 8. Você vai perceber que, ao rolar o texto, a Lua não se move.
Etapa 2: fazer o MotionLayout rolar
- Para fazer com que a visualização
MotionLayoutrole assim queNestedScrollViewrolar, adicionemotion: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" >
- Execute o app novamente e vá para a Etapa 8. Você vai notar que o
MotionLayouté recolhido à medida que você rola para cima. No entanto, a animação ainda não avança com base no comportamento de rolagem.
Etapa 3: mover o movimento com código
- Abra
Step8Activity.kt. Edite a funçãocoordinateMotion()para informarMotionLayoutsobre as mudanças na posição de rolagem.
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)
}
Esse código vai registrar um OnOffsetChangedListener que será chamado sempre que o usuário rolar com o deslocamento de rolagem atual.
O MotionLayout oferece suporte à busca da transição definindo a propriedade de progresso. Para converter entre um verticalOffset e uma porcentagem de progresso, divida pelo intervalo total de rolagem.
Fazer um teste
- Implante o app novamente e execute a animação da Etapa 8. Você vai notar que
MotionLayoutvai progredir a animação com base na posição de rolagem.

É possível criar animações personalizadas de barras de ferramentas dinâmicas recolhíveis usando MotionLayout. Usando uma sequência de KeyFrames, é possível alcançar efeitos muito ousados.
12. Parabéns
Este codelab abordou a API básica do MotionLayout.
Para ver mais exemplos de MotionLayout na prática, confira a amostra oficial. Confira a documentação.
Saiba mais
O MotionLayout oferece ainda mais recursos não abordados neste codelab, como KeyCycle,, que permite controlar caminhos ou atributos com ciclos repetidos, e KeyTimeCycle,, que permite animar com base na hora do relógio. Confira os exemplos de cada um.
Para acessar links de outros codelabs neste curso, consulte a página de destino dos codelabs do curso Android avançado no Kotlin.