Kotlin 03.2 での高度な Android: MotionLayout を使用したアニメーション

1. 始める前に

この Codelab は、Kotlin を使った高度な Android 開発コースの一部です。Codelab を順番に進めると、このコースを最大限に活用できますが、これは必須ではありません。コースの Codelab はすべて、Kotlin を使った高度な Android 開発の Codelab ランディング ページに一覧表示されています。

MotionLayout は、Android アプリにリッチなモーションを追加できるライブラリです。ConstraintLayout, に基づいており、ConstraintLayout を使用して作成できるものをアニメーション化できます。

MotionLayout を使用すると、複数のビューの場所、サイズ、可視性、アルファ値、色、高さ、回転などの属性を同時にアニメーション化できます。宣言型の XML を使用すると、複数のビューを含む調整されたアニメーションを作成できます。これは、コードでは実現が困難です。

アニメーションは、アプリのエクスペリエンスを向上させる優れた方法です。アニメーションを使用すると、次のことができます。

  • 変化を表示する - 状態間のアニメーションにより、ユーザーは UI の変化を自然に追跡できます。
  • 注意を引く - アニメーションを使用して、重要な UI 要素に注意を引きます。
  • 美しいデザインを構築する - デザインに効果的なモーションを取り入れることで、アプリの見栄えがよくなります。

前提条件

この Codelab は、Android 開発の経験があるデベロッパーを対象としています。この Codelab を始める前に、次のことを確認してください。

  • アクティビティと基本的なレイアウトを含むアプリを作成し、Android Studio を使用してデバイスまたはエミュレータで実行する方法を理解している。ConstraintLayout について理解を深めておきましょう。ConstraintLayout について詳しくは、Constraint Layout の Codelab をご覧ください。

演習内容

  • ConstraintSetsMotionLayout を使用してアニメーションを定義する
  • ドラッグ イベントに基づいてアニメーション化する
  • KeyPosition でアニメーションを変更する
  • KeyAttribute で属性を変更する
  • コードでアニメーションを実行する
  • MotionLayout で折りたたみ可能なヘッダーをアニメーション化する

必要なもの

  • Android Studio 4.0MotionLayout エディタはこのバージョンの Android Studio でのみ動作します)。

2. はじめに

サンプルアプリを次のいずれかの方法でダウンロードします。

または次のコマンドを使用して、コマンドラインから GitHub リポジトリのクローンを作成します。

$ git clone https://github.com/googlecodelabs/motionlayout.git

3. MotionLayout を使用してアニメーションを作成する

まず、ユーザーのクリックに応じて画面の上部から下部までビューを移動させるアニメーションを作成します。

スターター コードからアニメーションを作成するには、次の主要な要素が必要です。

  • ConstraintLayout のサブクラスである MotionLayout,。アニメーション化するすべてのビューは MotionLayout タグ内で指定します。
  • MotionLayout. のアニメーションを記述する XML ファイルである MotionScene,
  • MotionScene の一部である Transition,。アニメーションの継続時間、トリガー、ビューの移動方法を指定します。
  • 遷移の開始終了の両方の制約を指定する ConstraintSet

MotionLayout から順に見ていきましょう。

ステップ 1: 既存のコードを確認する

MotionLayoutConstraintLayout のサブクラスであるため、アニメーションを追加しながら同じ機能をすべてサポートします。MotionLayout を使用するには、ConstraintLayout. を使用する場所に MotionLayout ビューを追加します。

  1. res/layoutactivity_step1.xml. を開きます。ここでは、星の 1 つの ImageView を含む ConstraintLayout があり、その中に色合いが適用されています。

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>

この ConstraintLayout には制約がないため、この時点でアプリを実行すると、星が制約なしで表示されます。つまり、星が不明な場所に配置されます。Android Studio に、制約がないことに関する警告が表示されます。

ステップ 2: Motion Layout に変換する

MotionLayout, を使用してアニメーション化するには、ConstraintLayoutMotionLayout に変換する必要があります。

レイアウトでモーション シーンを使用するには、モーション シーンを指す必要があります。

  1. これを行うには、デザイン サーフェスを開きます。Android Studio 4.0 では、レイアウト XML ファイルを表示しているときに、右上の分割アイコンまたはデザイン アイコンを使用してデザイン サーフェスを開きます。

a2beea710c2decb7.png

  1. デザイン サーフェスを開いたら、プレビューを右クリックして [Convert to MotionLayout] を選択します。

4fa936a98a8393b9.png

これにより、ConstraintLayout タグが MotionLayout タグに置き換えられ、@xml/activity_step1_scene. を指す motion:layoutDescriptionMotionLayout タグに追加されます。

activity_step1**.xml**

<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:layoutDescription="@xml/activity_step1_scene">

モーション シーンは、MotionLayout のアニメーションを記述する単一の XML ファイルです。

MotionLayout に変換すると、デザイン サーフェスに Motion Editor が表示されます。

66d0e80d5ab4daf8.png

モーション エディタには、次の 3 つの新しい UI 要素があります。

  1. 概要 - アニメーションのさまざまな部分を選択できるモーダル選択です。この画像では、start ConstraintSet が選択されています。startend の間の矢印をクリックして、それらの間の切り替え効果を選択することもできます。
  2. セクション - 概要の下に、現在選択されている概要項目に基づいて変化するセクション ウィンドウが表示されます。この画像では、選択ウィンドウに start ConstraintSet 情報が表示されています。
  3. 属性 - 属性パネルには、概要ウィンドウまたは選択ウィンドウで現在選択されているアイテムの属性が表示され、編集できます。この画像は、start ConstraintSet の属性を示しています。

ステップ 3: 開始と終了の制約を定義する

すべてのアニメーションは、開始と終了で定義できます。start はアニメーション前の画面の状態を表し、end はアニメーション後の画面の状態を表します。MotionLayout は、開始状態と終了状態の間をアニメーション化する方法(時間経過)を決定します。

MotionSceneConstraintSet タグを使用して開始状態と終了状態を定義します。ConstraintSet は、ビューに適用できる制約のセットです。これには、幅、高さ、ConstraintLayout の制約が含まれます。また、alpha などの属性も含まれます。ビュー自体は含まれておらず、ビューに対する制約のみが含まれています。

ConstraintSet で指定された制約は、レイアウト ファイルで指定された制約をオーバーライドします。レイアウトと MotionScene の両方で制約を定義した場合、MotionScene の制約のみが適用されます。

このステップでは、星のビューが画面の上部から始まり、画面の下部で終わるように制約します。

この手順は、モーション エディタを使用するか、activity_step1_scene.xml のテキストを直接編集して完了できます。

  1. 概要パネルで start ConstraintSet を選択します。

6e57661ed358b860.png

  1. [選択] パネルで、red_star を選択します。現在は「Source of layout」と表示されています。これは、この ConstraintSet で制約されていないことを意味します。右上の鉛筆アイコンを使用して、制約を作成します。

f9564c574b86ea8.gif

  1. 概要パネルで start ConstraintSet が選択されている場合、red_star にソース start が表示されることを確認します。
  2. [Attributes] パネルで、start ConstraintSetred_star を選択した状態で、上部に制約を追加し、青い [+] ボタンをクリックして開始します。

2fce076cd7b04bd.png

  1. xml/activity_step1_scene.xml を開いて、モーション エディタがこの制約のために生成したコードを確認します。

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>

ConstraintSetid@id/start で、MotionLayout のすべてのビューに適用するすべての制約を指定します。この MotionLayout にはビューが 1 つしかないため、必要な Constraint は 1 つだけです。

ConstraintSet 内の Constraint は、制約するビューの ID(activity_step1.xml で定義された @id/red_star)を指定します。Constraint タグは制約とレイアウト情報のみを指定します。Constraint タグは、ImageView に適用されていることを認識していません。

この制約は、red_star ビューを親の左上に制約するために必要な高さ、幅、その他の 2 つの制約を指定します。

  1. 概要パネルで end ConstraintSet を選択します。

346e1248639b6f1e.png

  1. 前と同じ手順で、end ConstraintSetred_starConstraint を追加します。
  2. モーション エディタを使用してこの手順を完了するには、青色の + ボタンをクリックして、bottomend に制約を追加します。

fd33c779ff83c80a.png

  1. XML のコードは次のようになります。

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>

@id/start と同様に、この ConstraintSet には @id/red_star に単一の Constraint があります。今回は、画面の下端に制約します。

@id/start@id/end という名前を付ける必要はありませんが、そうすると便利です。

ステップ 4: 画面切り替えを定義する

また、すべての MotionScene には少なくとも 1 つのトランジションを含める必要があります。遷移は、アニメーションの開始から終了までのすべての部分を定義します。

遷移では、遷移の開始と終了の ConstraintSet を指定する必要があります。遷移では、アニメーションの実行時間やビューをドラッグしてアニメーション化する方法など、アニメーションを変更する方法を他の方法で指定することもできます。

  1. Motion Editor は、MotionScene ファイルの作成時にデフォルトでトランジションを作成しました。activity_step1_scene.xml を開いて、生成されたトランジションを確認します。

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>

これは、MotionLayout がアニメーションを構築するために必要なすべてのものです。各属性を見てみましょう。

  • アニメーションの開始時に、constraintSetStart がビューに適用されます。
  • constraintSetEnd はアニメーションの最後にビューに適用されます。
  • duration は、アニメーションにかかる時間をミリ秒単位で指定します。

MotionLayout は、開始制約と終了制約の間のパスを計算し、指定された期間でアニメーション化します。

ステップ 5: モーション エディタでアニメーションをプレビューする

dff9ecdc1f4a0740.gif

アニメーション: モーション エディタで切り替え効果のプレビューを再生する動画

  1. モーション エディタを開き、概要パネルの startend の間の矢印をクリックして、切り替えを選択します。

1dc541ae8c43b250.png

  1. トランジションを選択すると、[選択] パネルに再生コントロールとスクラブバーが表示されます。再生ボタンをクリックするか、現在の位置をドラッグしてアニメーションをプレビューします。

a0fd2593384dfb36.png

ステップ 6: クリック ハンドラを追加する

アニメーションを開始する方法が必要です。これを行う方法の 1 つは、MotionLayout@id/red_star のクリック イベントに応答するようにすることです。

  1. モーション エディタを開き、概要パネルの開始と終了の間の矢印をクリックしてトランジションを選択します。

b6f94b344ce65290.png

  1. 概要パネルのツールバーで 699f7ae04024ccf6.png [クリックまたはスワイプ ハンドラを作成] をクリックします。これにより、トランジションを開始するハンドラが追加されます。
  2. ポップアップで [クリック ハンドラ] を選択します。

ccf92d06335105fe.png

  1. [View To Click] を red_star に変更します。

b0d3f0c970604f01.png

  1. [追加] をクリックします。クリック ハンドラは、Motion Editor の Transition 上の小さな点で表されます。

cec3913e67fb4105.png

  1. 概要パネルでトランジションを選択した状態で、属性パネルで追加した OnClick ハンドラに toggleclickAction 属性を追加します。

9af6fc60673d093d.png

  1. activity_step1_scene.xml を開いて、モーション エディタで生成されたコードを確認します。

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 は、<OnClick> タグを使用してクリック イベントに応じてアニメーションを実行するよう MotionLayout に指示します。各属性を見てみましょう。

  • targetId は、クリックを監視するビューです。
  • toggleclickAction は、クリックすると開始状態と終了状態を切り替えます。clickAction のその他のオプションについては、ドキュメントをご覧ください。
  1. コードを実行し、[ステップ 1] をクリックしてから、赤い星をクリックすると、アニメーションが表示されます。

ステップ 5: アニメーションの動作

アプリを実行します。星をクリックすると、アニメーションが実行されます。

7ba88af963fdfe10.gif

完成したモーション シーン ファイルは、開始と終了の ConstraintSet を指す 1 つの Transition を定義します。

アニメーションの開始時(@id/start)、星アイコンは画面の上端に制約されています。アニメーションの終了時(@id/end)に、星アイコンは画面の下端に制約されます。

<?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. ドラッグ イベントに基づくアニメーション

このステップでは、ユーザーのドラッグ イベント(ユーザーが画面をスワイプしたとき)に応答してアニメーションを実行するアニメーションを構築します。MotionLayout は、ビューを移動するためのタッチイベントのトラッキングと、物理ベースのフリング ジェスチャーをサポートし、モーションを滑らかにします。

ステップ 1: 初期コードを調べる

  1. まず、既存の MotionLayout を含むレイアウト ファイル activity_step2.xml を開きます。コードを見てみましょう。

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>

このレイアウトは、アニメーションのすべてのビューを定義します。3 つの星のアイコンは、モーション シーンでアニメーション化されるため、レイアウトで制約されていません。

クレジット TextView には制約が適用されています。アニメーション全体で同じ場所に留まり、属性を変更しないためです。

ステップ 2: シーンをアニメーション化する

前回のアニメーションと同様に、アニメーションは開始と終了の ConstraintSet,Transition で定義されます。

開始 ConstraintSet を定義する

  1. モーション シーン xml/step2.xml を開いてアニメーションを定義します。
  2. 開始制約 start の制約を追加します。開始時には、3 つの星がすべて画面の下部中央に配置されます。左右の星の alpha 値は 0.0 で、完全に透明で非表示であることを意味します。

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>

この ConstraintSet では、星ごとに 1 つの Constraint を指定します。各制約は、アニメーションの開始時に MotionLayout によって適用されます。

各星のビューは、開始、終了、下部の制約を使用して画面の下部中央に配置されます。2 つの星 @id/left_star@id/right_star には、アニメーションの開始時に適用される、不可視にするためのアルファ値が追加されています。

startend の制約セットは、アニメーションの開始と終了を定義します。motion:layout_constraintStart_toStartOf などの開始の制約は、ビューの開始を別のビューの開始に制限します。start という名前が両方に使用され、両方が制約のコンテキストで使用されるため、最初は混乱する可能性があります。この違いを明確にするため、layout_constraintStartstart はビューの「開始」を指します。これは、左から右に向かう言語では左側、右から左に向かう言語では右側になります。start 制約セットは、アニメーションの開始を指します。

終了 ConstraintSet を定義する

  1. チェーンを使用して、3 つの星を @id/credits の下にまとめて配置するように終了制約を定義します。また、左右の星の alpha の終了値を 1.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>

結果として、ビューはアニメーション化されると中心から外側に向かって広がります。

また、alpha プロパティは両方の ConstraintSets@id/right_start@id/left_star に設定されているため、アニメーションの進行に合わせて両方のビューがフェードインします。

ユーザーのスワイプに基づくアニメーション

MotionLayout は、ユーザーのドラッグ イベントやスワイプをトラッキングして、物理ベースの「フリング」アニメーションを作成できます。つまり、ユーザーがフリングするとビューは動き続け、物理的な物体が表面を転がるように減速します。このタイプのアニメーションは、TransitionOnSwipe タグで追加できます。

  1. OnSwipe タグを追加する TODO を <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 にはいくつかの属性が含まれていますが、最も重要なのは touchAnchorId です。

  • touchAnchorId は、タップに応じて移動する追跡対象のビューです。MotionLayout は、このビューをスワイプしている指から同じ距離に保ちます。
  • touchAnchorSide は、ビューのどちら側をトラッキングするかを決定します。これは、サイズ変更を行うビュー、複雑なパスをたどるビュー、片側がもう一方より速く移動するビューで重要です。
  • dragDirection は、このアニメーションでどの方向が重要か(上、下、左、右)を決定します。

MotionLayout がドラッグ イベントをリッスンする場合、リスナーは touchAnchorId で指定されたビューではなく、MotionLayout ビューに登録されます。ユーザーが画面の任意の場所でジェスチャーを開始すると、MotionLayout は指と touchAnchorId ビューの touchAnchorSide の間の距離を一定に保ちます。たとえば、アンカーの端から 100dp 離れた位置をタップすると、MotionLayout はアニメーション全体を通して、その端から 100dp 離れた位置を維持します。

試してみる

  1. アプリを再度実行し、ステップ 2 の画面を開きます。アニメーションが表示されます。
  2. アニメーションの途中で指を「フリック」したり離したりして、MotionLayout が流体物理学に基づいたアニメーションをどのように表示するかを試してみてください。

fefcdd690a0dcaec.gif

MotionLayout は、ConstraintLayout の機能を使用してリッチな効果を生み出し、大きく異なるデザイン間をアニメーション化できます。

このアニメーションでは、3 つのビューはすべて、画面の下部にある親に対して相対的に配置されています。最終的に、3 つのビューは @id/credits を基準としてチェーン内に配置されます。

レイアウトは大きく異なりますが、MotionLayout は開始と終了の間で流れるようなアニメーションを作成します。

5. パスの変更

このステップでは、アニメーション中に複雑なパスをたどり、モーション中にクレジットをアニメーション化するアニメーションを作成します。MotionLayout は、KeyPosition を使用して、ビューが開始点と終了点の間を移動するパスを変更できます。

ステップ 1: 既存のコードを確認する

  1. layout/activity_step3.xmlxml/step3.xml を開いて、既存のレイアウトとモーション シーンを確認します。ImageViewTextView には月とクレジットのテキストが表示されます。
  2. モーション シーン ファイル(xml/step3.xml)を開きます。@id/start から @id/end までの Transition が定義されていることがわかります。アニメーションでは、2 つの ConstraintSets を使用して、画面の左下から右下へ月の画像を移動します。月が動くにつれて、クレジットのテキストが alpha="0.0" から alpha="1.0" にフェードインします。
  3. アプリを実行し、[Step 3] を選択します。月をクリックすると、月が開始から終了まで直線状の経路をたどることがわかります。

ステップ 2: パスのデバッグを有効にする

月の動きに弧を追加する前に、MotionLayout でパスのデバッグを有効にしておくと便利です。

MotionLayout を使用して複雑なアニメーションを開発する際に、各ビューのアニメーション パスを描画できます。これは、アニメーションを視覚化したり、モーションの細部を微調整したりする場合に便利です。

  1. デバッグパスを有効にするには、layout/activity_step3.xml を開き、MotionLayout タグに motion:motionDebug="SHOW_PATH" を追加します。

activity_step3.xml

<!-- Add motion:motionDebug="SHOW_PATH" -->

<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:motionDebug="SHOW_PATH" >

パスのデバッグを有効にしてアプリを再度実行すると、すべてのビューのパスが点線で可視化されます。

23bbb604f456f65c.png

  • は、1 つのビューの開始位置または終了位置を表します。
  • は、1 つのビューのパスを表します。
  • ひし形は、パスを変更する KeyPosition を表します。

たとえば、このアニメーションでは、中央の円がクレジット テキストの位置です。

ステップ 3: パスを変更する

MotionLayout のアニメーションはすべて、アニメーションの開始前と終了後の画面の状態を定義する開始 ConstraintSet と終了 ConstraintSet で定義されます。デフォルトでは、MotionLayout は位置が変化する各ビューの開始位置と終了位置の間に線形パス(直線)をプロットします。

この例の月の弧のような複雑なパスを構築するために、MotionLayoutKeyPosition を使用して、ビューが開始点と終了点の間でたどるパスを変更します。

  1. xml/step3.xml を開き、KeyPosition をシーンに追加します。KeyPosition タグは Transition タグ内に配置されます。

eae4dae9a12d0410.png

step3.xml

<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
   />
</KeyFrameSet>

KeyFrameSetTransition の子であり、トランジション中に適用される KeyPosition などのすべての KeyFrames のセットです。

MotionLayout は、開始と終了の間の月のパスを計算するときに、KeyFrameSet で指定された KeyPosition に基づいてパスを変更します。アプリを再度実行すると、パスが変更されていることがわかります。

KeyPosition には、パスの変更方法を示すいくつかの属性があります。最も重要なものは次のとおりです。

  • framePosition は 0 ~ 100 の数値です。アニメーションのどの時点でこの KeyPosition を適用するかを定義します。1 はアニメーションの 1%、99 はアニメーションの 99% を意味します。値が 50 の場合は、ちょうど真ん中に適用します。
  • motionTarget は、この KeyPosition がパスを変更するビューです。
  • keyPositionType は、この KeyPosition がパスを変更する方法です。parentRelativepathRelativedeltaRelative のいずれかになります(次のステップで説明します)。
  • percentX | percentY は、framePosition でパスをどの程度変更するかを示します(0.0 ~ 1.0 の値。負の値と 1 より大きい値も使用できます)。

framePosition で、motionTarget のパスを percentX または percentY だけ移動して変更する。keyPositionType

デフォルトでは、MotionLayout はパスの変更によって導入された角を丸めます。作成したアニメーションを見ると、月がカーブした軌道を描いていることがわかります。ほとんどのアニメーションでは、この動作が望ましいものです。そうでない場合は、curveFit 属性を指定してカスタマイズできます。

試してみる

アプリを再度実行すると、このステップのアニメーションが表示されます。

46b179c01801f19e.gif

月は、Transition で指定された KeyPosition を通過するため、弧を描きます。

<KeyPosition
       motion:framePosition="50"
       motion:motionTarget="@id/moon"
       motion:keyPositionType="parentRelative"
       motion:percentY="0.5"
/>

この KeyPosition は、「framePosition 50(アニメーションの途中)で、motionTarget @id/moon のパスを parentRelativeMotionLayout 全体)で決定された座標に従って 50% Y(画面の半分)だけ移動して変更する」と解釈できます。

アニメーションの途中で、月が画面の 50% 下にある KeyPosition を通過する必要があります。この KeyPosition は X 軸の動きをまったく変更しないため、月は引き続き水平方向に最初から最後まで移動します。MotionLayout は、開始点と終了点の間を移動しながらこの KeyPosition を通過するスムーズな経路を計算します。

よく見ると、クレジットのテキストは月の位置によって制約されています。なぜ垂直方向にも移動しないのですか?

1c7cf779931e45cc.gif

<Constraint
       android:id="@id/credits"
       ...
       motion:layout_constraintBottom_toBottomOf="@id/moon"
       motion:layout_constraintTop_toTopOf="@id/moon"
/>

月の軌道を修正しても、月の開始位置と終了位置は垂直方向に移動しないことがわかりました。KeyPosition は開始位置と終了位置を変更しないため、クレジット テキストは月の最終的な終了位置に制約されます。

クレジットを月と一緒に移動させたい場合は、クレジットに KeyPosition を追加するか、@id/credits の開始制約を変更します。

次のセクションでは、MotionLayout のさまざまなタイプの keyPositionType について詳しく説明します。

6. keyPositionType について

最後のステップでは、parentRelativekeyPosition 型を使用して、パスを画面の 50% オフセットしました。属性 keyPositionType は、percentX または percentY に従って MotionLayout がパスをどのように変更するかを決定します。

<KeyFrameSet>
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
   />
</KeyFrameSet>

keyPosition には、parentRelativepathRelativedeltaRelative の 3 種類があります。型を指定すると、percentXpercentY の計算に使用される座標系が変わります。

座標系とは

座標系は、空間内の点を指定する方法を提供します。画面上の位置を説明する場合にも便利です。

MotionLayout 座標系は直交座標系です。つまり、2 つの垂直線で定義された X 軸と Y 軸があります。主な違いは、画面上の X 軸の位置です(Y 軸は常に X 軸に垂直です)。

MotionLayout のすべての座標系では、X 軸と Y 軸の両方で 0.01.0 の値が使用されます。負の値と 1.0 より大きい値を使用できます。たとえば、percentX の値が -2.0 の場合、X 軸の反対方向に 2 回移動します。

代数の授業みたいに感じた方は、下の画像をご覧ください。

parentRelative 座標

a7b7568d46d9dec7.png

parentRelativekeyPositionType は、画面と同じ座標系を使用します。(0, 0)MotionLayout 全体の左上に、(1, 1) は右下に定義されます。

parentRelative は、この例の月の弧のように、MotionLayout 全体を移動するアニメーションを作成する場合に使用できます。

ただし、モーションを基準としたパスを変更する場合(たとえば、パスを少しだけカーブさせる場合)は、他の 2 つの座標系を使用することをおすすめします。

deltaRelative 座標

5680bf553627416c.png

デルタは変化を表す数学用語であるため、deltaRelative は「相対的な変化」を表す方法です。deltaRelative 座標では、(0,0) はビューの開始位置、(1,1) は終了位置です。X 軸と Y 軸は画面に沿って配置されます。

X 軸は常に画面上で水平になり、Y 軸は常に画面上で垂直になります。parentRelative との主な違いは、座標がビューの移動先の画面の一部のみを表すことです。

deltaRelative は、水平方向または垂直方向の動きを個別に制御するのに最適な座標系です。たとえば、垂直方向(Y)の移動を 50% で完了し、水平方向(X)のアニメーションを継続するアニメーションを作成できます。

pathRelative 座標

f3aaadaac8b4a93f.png

MotionLayout の最後の座標系は pathRelative です。X 軸はモーションパスを最初から最後までたどるため、他の 2 つとは大きく異なります。したがって、(0,0) は開始位置、(1,0) は終了位置です。

なぜこのようなことをしたいのでしょうか?この座標系は画面座標系とも一致していないため、一見すると非常に奇妙に思えます。

pathRelative は、いくつかの点で非常に便利です。

  • アニメーションの一部でビューの速度を上げたり、下げたり、停止したりする。X ディメンションはビューがたどるパスと常に完全に一致するため、pathRelative KeyPosition を使用して、そのパスの特定のポイントに到達する framePosition を変更できます。したがって、framePosition="50"KeyPositionpercentX="0.1" を使用すると、アニメーションは動作の最初の 10% を移動するのに 50% の時間を要します。
  • パスに微妙な弧を追加します。Y 軸は常に動きに対して垂直であるため、Y を変更すると、全体的な動きに対するパスの曲線が変わります。
  • deltaRelative の場合、2 つ目のディメンションを追加することはできません。完全に水平または垂直の動きの場合、deltaRelative は 1 つの有用なディメンションのみを作成します。ただし、pathRelative は常に使用可能な X 座標と Y 座標を作成します。

次のステップでは、複数の KeyPosition を使用して、さらに複雑なパスを構築する方法について説明します。

7. 複雑なパスの構築

前の手順で作成したアニメーションを見ると、滑らかな曲線が作成されていますが、形状は「月」のようにもっと丸くすることができます。

複数の KeyPosition 要素を含むパスを変更する

MotionLayout は、必要な数の KeyPosition を定義して、任意のモーションを取得することで、パスをさらに変更できます。このアニメーションでは弧を描きますが、必要に応じて、画面の中央で月が上下に跳ねるようにすることもできます。

  1. xml/step4.xml を開きます。同じビューと、前の手順で追加した KeyFrame が表示されます。
  2. 曲線の頂点を丸くするには、@id/moon のパスに 2 つの KeyPositions を追加します。1 つは頂点に達する直前、もう 1 つは頂点に達した後です。

500b5ac2db48ef87.png

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

これらの KeyPositions はアニメーションの 25% と 75% の時点で適用され、@id/moon が画面の上から 60% の位置にあるパスを移動します。既存の KeyPosition(50%)と組み合わせることで、月がたどる滑らかな弧が作成されます。

MotionLayout では、目的のモーション パスを取得するために必要な数の KeyPositions を追加できます。MotionLayout は、指定された framePosition で各 KeyPosition を適用し、すべての KeyPositions を通過するスムーズなモーションを作成する方法を把握します。

試してみる

  1. アプリを再度実行すると、ステップ 4 に進んで、アニメーションの動作を確認します。月をクリックすると、KeyFrameSet で指定された各 KeyPosition を通過しながら、開始点から終了点までのパスをたどります。

自分で設定する

他のタイプの KeyFrame に進む前に、KeyFrameSetKeyPositions を追加して、KeyPosition だけでどのような効果を作成できるかを確認してください。

アニメーション中に前後に移動する複雑なパスを作成する方法を示す例を次に示します。

cd9faaffde3dfef.png

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>

KeyPosition の探索が完了したら、次のステップで他のタイプの KeyFrames に進みます。

8. モーション中の属性の変更

動的なアニメーションを構築するとは、アニメーションの進行に合わせてビューの sizerotationalpha を変更することを意味します。MotionLayout は、KeyAttribute を使用して、任意のビューのさまざまな属性のアニメーション化をサポートしています。

このステップでは、KeyAttribute を使用して月を拡大縮小し、回転させます。また、KeyAttribute を使用して、月がほぼ一周するまでテキストの表示を遅らせます。

ステップ 1: KeyAttribute を使用してサイズ変更と回転を行う

  1. 前の手順で作成したアニメーションと同じアニメーションを含む xml/step5.xml を開きます。この画面では、背景に別の宇宙の写真を使用しています。
  2. 月を拡大して回転させるには、keyFrame="50"keyFrame="100"KeyFrameSet に 2 つの KeyAttribute タグを追加します。

bbae524a2898569.png

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

これらの KeyAttributes は、アニメーションの 50% と 100% で適用されます。最初の KeyAttribute(50%)は円弧の頂点で発生し、ビューのサイズが 2 倍になり、-360 度(1 回転)回転します。2 つ目の KeyAttribute は、2 回目の回転を -720 度(2 周)まで完了し、scaleXscaleY の値がデフォルトで 1.0 に設定されているため、サイズを通常のサイズに戻します。

KeyPosition と同様に、KeyAttributeframePositionmotionTarget を使用して、KeyFrame を適用するタイミングと、変更するビューを指定します。MotionLayoutKeyPositions の間を補間して、滑らかなアニメーションを作成します。

KeyAttributes は、すべてのビューに適用できる属性をサポートしています。visibilityalphaelevation などの基本属性の変更をサポートしています。ここで示すように回転を変更したり、rotateXrotateY で 3 次元で回転させたり、scaleXscaleY でサイズを拡大縮小したり、X、Y、Z でビューの位置を移動したりすることもできます。

ステップ 2: クレジットの表示を遅らせる

このステップの目標の 1 つは、アニメーションがほぼ完了するまでクレジット テキストが表示されないようにアニメーションを更新することです。

  1. クレジットの表示を遅らせるには、keyPosition="85" まで alpha が 0 のままになるようにする KeyAttribute をもう 1 つ定義します。MotionLayout は 0 から 100 のアルファにスムーズに移行しますが、アニメーションの最後の 15% で移行します。

step5.xml

<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->

<KeyAttribute
       motion:framePosition="85"
       motion:motionTarget="@id/credits"
       android:alpha="0.0"
/>

この KeyAttribute は、アニメーションの最初の 85% の間、@id/creditsalpha を 0.0 に保ちます。アルファ値が 0 から始まるため、アニメーションの最初の 85% は非表示になります。

この KeyAttribute の最終的な効果は、アニメーションの終わりにクレジットが表示されることです。これにより、画面の右下に月が収まるように調整されているように見えます。

このように、あるビューのアニメーションを遅らせて別のビューを動かすことで、ユーザーにダイナミックな印象を与えるアニメーションを作成できます。

試してみる

  1. アプリを再度実行し、ステップ 5 に移動して、アニメーションが動作していることを確認します。月をクリックすると、開始から終了までのパスに沿って、KeyFrameSet で指定された各 KeyAttribute を通過します。

2f4bfdd681c1fa98.gif

月を 2 回転させたので、月はダブル バックフリップを行い、アニメーションがほぼ終了するまでクレジットの表示が遅れます。

自分で設定する

KeyFrame の最後のタイプに進む前に、KeyAttributes で他の標準の属性を変更してみてください。たとえば、rotationrotationX に変更して、どのようなアニメーションが生成されるかを確認してみてください。

試すことができる標準属性のリストは次のとおりです。

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

9. カスタム属性の変更

リッチ アニメーションでは、ビューの色やその他の属性を変更します。MotionLayoutKeyAttribute を使用して前のタスクに記載されている標準属性を変更できますが、CustomAttribute を使用して他の属性を指定します。

CustomAttribute を使用して、セッターを持つ任意の値の設定が可能です。たとえば、CustomAttribute を使用して View の backgroundColor を設定できます。MotionLayoutリフレクションを使用してセッターを見つけ、それを繰り返し呼び出してビューをアニメーション化します。

このステップでは、CustomAttribute を使用して月の colorFilter 属性を設定し、次のアニメーションを作成します。

5fb6792126a09fda.gif

カスタム属性を定義する

  1. まず、前の手順で作成したアニメーションと同じアニメーションを含む xml/step6.xml を開きます。
  2. 月が色を変えるようにするには、keyFrame="0"keyFrame="50"keyFrame="100".KeyFrameSetCustomAttribute を含む 2 つの KeyAttribute を追加します。

214699d5fdd956da.png

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>

KeyAttribute 内に CustomAttribute を追加します。CustomAttribute は、KeyAttribute で指定された framePosition に適用されます。

CustomAttribute 内で、attributeName と設定する値を 1 つ指定する必要があります。

  • motion:attributeName は、このカスタム属性によって呼び出されるセッターの名前です。この例では、DrawablesetColorFilter が呼び出されます。
  • motion:custom*Value は、名前で指定された型のカスタム値です。この例では、カスタム値は指定された色です。

カスタム値の型は次のいずれかになります。

  • 整数
  • 浮動小数点数
  • 文字列
  • ディメンション
  • ブール値

この API を使用すると、MotionLayout は任意のビューでセッターを提供するものをアニメーション化できます。

試してみる

  1. アプリを再度実行し、ステップ 6 に移動して、アニメーションが動作していることを確認します。月をクリックすると、開始から終了までのパスに沿って、KeyFrameSet で指定された各 KeyAttribute を通過します。

5fb6792126a09fda.gif

KeyFrames を追加すると、MotionLayout は月の軌道を直線から複雑な曲線に変更し、アニメーションの途中でダブル バックフリップ、サイズ変更、色の変更を追加します。

実際のアニメーションでは、複数のビューを同時にアニメーション化し、異なるパスと速度でモーションを制御することがよくあります。各ビューに異なる KeyFrame を指定することで、MotionLayout を使用して複数のビューをアニメーション化するリッチなアニメーションを演出できます。

10. ドラッグ イベントと複雑なパス

このステップでは、複雑なパスで OnSwipe を使用する方法について説明します。これまで、月の動きは OnClick リスナーによってトリガーされ、一定時間実行されていました。

OnSwipe を使用して複雑なパスを持つアニメーション(前の手順で作成した月のアニメーションなど)を制御するには、OnSwipe の仕組みを理解する必要があります。

ステップ 1: OnSwipe の動作を調べる

  1. xml/step7.xml を開き、既存の OnSwipe 宣言を見つけます。

step7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
/>
  1. デバイスでアプリを実行し、ステップ 7 に進みます。月のアイコンを円弧のパスに沿ってドラッグして、スムーズなアニメーションを作成できるかどうかを確認します。

このアニメーションを実行すると、見た目がよくありません。月が弧の頂点に達すると、跳ね回り始めます。

ed96e3674854a548.gif

このバグを理解するために、ユーザーが円弧の上端のすぐ下をタップしたときに何が起こるかを考えてみましょう。OnSwipe タグに motion:touchAnchorSide="bottom" があるため、MotionLayout はアニメーション全体を通して指とビューの下部の距離を一定に保とうとします。

しかし、月の下部が常に同じ方向に移動するわけではなく、上方向に移動してから下方向に移動するため、ユーザーが弧の頂点を通過した直後に MotionLayout が何をすべきかわからなくなります。これを考慮すると、月の下部をトラッキングしているため、ユーザーがここをタッチしたときに、どこに配置されるべきでしょうか?

56cd575c5c77eddd.png

ステップ 2: 右側を使用する

このようなバグを回避するには、アニメーションの全期間にわたって常に一方向に進行する touchAnchorIdtouchAnchorSide を常に選択することが重要です。

このアニメーションでは、月の right 側と left 側の両方が画面上を同じ方向に進みます。

ただし、bottomtop の両方が方向を反転します。OnSwipe が追跡しようとすると、方向が変わったときに混乱します。

  1. このアニメーションをタッチイベントに追従させるには、touchAnchorSideright に変更します。

step7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="right"
/>

ステップ 3: dragDirection を使用する

dragDirectiontouchAnchorSide を組み合わせて、通常とは異なる方向に分岐させることもできます。touchAnchorSide が一方向にのみ進むことは引き続き重要ですが、MotionLayout に追跡する方向を指示できます。たとえば、touchAnchorSide="bottom" を保持したまま dragDirection="dragRight" を追加できます。これにより、MotionLayout はビューの下部の位置を追跡しますが、右に移動するときにのみその位置を考慮します(垂直方向の動きは無視します)。そのため、下部が上下に移動しても、OnSwipe で正しくアニメーション化されます。

  1. 月の動きを正しくトラッキングするように OnSwipe を更新。

step7.xml

<!-- Using dragDirection to control the direction of drag tracking →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
       motion:dragDirection="dragRight"
/>

試してみる

  1. アプリを再度実行し、月をパス全体にドラッグしてみます。複雑な弧を描く場合でも、MotionLayout はスワイプ イベントに応じてアニメーションを進めることができます。

5458dff382261427.gif

11. コードでモーションを実行する

MotionLayout は、CoordinatorLayout と組み合わせて使用すると、リッチなアニメーションを構築するために使用できます。このステップでは、MotionLayout を使用して折りたたみ可能なヘッダーを作成します。

ステップ 1: 既存のコードを確認する

  1. まず、layout/activity_step8.xml を開きます。
  2. layout/activity_step8.xml では、動作する CoordinatorLayoutAppBarLayout がすでにビルドされていることがわかります。

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>

このレイアウトでは、CoordinatorLayout を使用して、NestedScrollViewAppBarLayout の間でスクロール情報を共有します。そのため、NestedScrollView が上にスクロールすると、AppBarLayout に変更が通知されます。このようにして、Android でこのような折りたたみ可能なツールバーを実装します。テキストのスクロールは、折りたたみ可能なヘッダーと「連携」します。

@id/motion_layout が指すモーション シーンは、最後のステップのモーション シーンと似ています。ただし、CoordinatorLayout と連携できるように、OnSwipe 宣言は削除されました。

  1. アプリを実行し、ステップ 8 に進みます。テキストをスクロールしても月が動かないことがわかります。

ステップ 2: MotionLayout をスクロール可能にする

  1. NestedScrollView がスクロールされたらすぐに MotionLayout ビューをスクロールするには、MotionLayoutmotion:minHeightmotion:layout_scrollFlags を追加します。

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"  >
  1. アプリを再度実行し、ステップ 8 に進みます。上にスクロールすると MotionLayout が折りたたまれることがわかります。ただし、アニメーションはまだスクロール動作に基づいて進行しません。

ステップ 3: コードを使用してモーションを移動する

  1. Step8Activity.kt を開きます。coordinateMotion() 関数を編集して、スクロール位置の変更を MotionLayout に通知します。

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

このコードは、ユーザーがスクロールするたびに現在のスクロール オフセットで呼び出される OnOffsetChangedListener を登録します。

MotionLayout は、進行状況プロパティを設定することで、遷移のシークをサポートします。verticalOffset と進行状況の割合を変換するには、スクロール範囲の合計で割ります。

試してみる

  1. アプリを再度デプロイして、ステップ 8 のアニメーションを実行します。MotionLayout がスクロール位置に基づいてアニメーションを進めることがわかります。

ee5ce4d9e33a59ca.gif

MotionLayout を使用して、カスタムの動的な折りたたみツールバー アニメーションを作成できます。KeyFrames のシーケンスを使用すると、非常に大胆な効果を実現できます。

12. 完了

この Codelab では、MotionLayout の基本的な API について説明しました。

MotionLayout の実際の使用例については、公式のサンプルをご覧ください。ドキュメントもぜひご覧ください。

詳細

MotionLayout は、この Codelab で説明していない KeyCycle,(繰り返しサイクルでパスや属性を制御できる)や KeyTimeCycle,(時計の時刻に基づいてアニメーション化できる)など、さらに多くの機能をサポートしています。各例については、サンプルをご覧ください。

このコースの他の Codelab へのリンクについては、Kotlin を使った高度な Android 開発の Codelab のランディング ページをご覧ください。