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 の詳細については、制約レイアウトの 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 タグ内に指定します。
  • MotionScene,MotionLayout. のアニメーションを記述する XML ファイルです。
  • MotionScene の一部である Transition,。アニメーションの持続時間、トリガー、ビューの移動方法を指定します。
  • 遷移の「開始」制約と「終了」制約の両方を指定する ConstraintSet

それでは、MotionLayout から順に、それぞれについて見ていきましょう。

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

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

  1. res/layoutactivity_step1.xml. を開きます。ここでは、内部に色合いが適用された星の ImageView が 1 つある 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: モーション レイアウトに変換する

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

Motion Editor には、次の 3 つの新しい UI 要素があります。

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

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

すべてのアニメーションは、開始と終了の観点から定義できます。start はアニメーション前の画面の様子を示し、end はアニメーションが完了した後の画面を示しています。MotionLayout は、開始状態と終了状態の間で(時間の経過に伴って)アニメーション化する方法を決定します。

MotionScene は、ConstraintSet タグを使用して開始状態と終了状態を定義します。ConstraintSet はその名のとおり、ビューに適用できる一連の制約です。これには、幅、高さ、ConstraintLayout の制約が含まれます。また、alpha などの属性も含まれます。これには、ビュー自体は含まれず、それらのビューに対する制約だけが含まれます。

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

このステップでは、スタービューの開始位置が画面の上端から開始し、終了位置が画面の下端に制限されるようにします。

この手順は、Motion Editor を使用するか、activity_step1_scene.xml のテキストを直接編集して完了できます。

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

6e57661ed358b860.png

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

f9564c574b86ea8.gif

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

2fce076cd7b04bd.png

  1. xml/activity_step1_scene.xml を開き、この制約に対して Motion Editor が生成したコードを確認します。

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. 以前と同じ手順に沿って、endConstraintSetred_starConstraint を追加します。
  2. Motion Editor を使用してこのステップを完了するには、青色の + ボタンをクリックして、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 に 1 つの Constraint があります。今度は画面の下端に固定されます。

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

ステップ 4: 遷移を定義する

また、すべての MotionScene には、少なくとも 1 つの遷移を含める必要があります。遷移は、開始から終了まで、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: Motion Editor でアニメーションをプレビューする

dff9ecdc1f4a0740.gif

アニメーション: Motion Editor で切り替え効果のプレビューを再生する動画

  1. Motion Editor を開き、概要パネルで 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. [Add] をクリックすると、クリック ハンドラが Motion Editor の [トランジション] に小さな点で表示されます。

cec3913e67fb4105.png

  1. 概要パネルで遷移を選択したら、先ほど属性パネルに追加した OnClick ハンドラに toggleclickAction 属性を追加します。

9af6fc60673d093d.png

  1. activity_step1_scene.xml を開き、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>

Transition は、<OnClick> タグを使用して、クリック イベントに応答してアニメーションを実行するよう MotionLayout に指示します。各属性の確認:

  • targetId は、クリックを監視するビューです。
  • toggleclickAction は、クリックすると開始状態と終了状態が切り替わります。clickAction のその他のオプションについては、ドキュメントをご覧ください。
  1. コードを実行して [Step 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 によって適用されます。

各スタービューは、開始、終了、下部の制約を使用して、画面の下部に中央寄せされます。@id/left_star@id/right_star の 2 つの星には、非表示になる追加のアルファ値があり、アニメーションの開始時に適用されます。

start 制約セットと end 制約セットは、アニメーションの開始と終了を定義します。motion:layout_constraintStart_toStartOf など、開始時の制約は、ビューの開始点を別のビューの開始点に制限します。これは、start という名前が両方に使用され、かつ両方とも制約のコンテキストで使用されるため、最初は混乱する可能性があります。区別を明確にするために、layout_constraintStartstart は「start」を参照しています。ビューでは、左から右に記述する言語、右から左に記述する言語、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 との距離を一定に保ちます。たとえば、MotionLayout は、アンカー側から 100 dp の距離に接触した場合、アニメーション全体で、アンカー側から指から 100 dp の距離を保ちます。

試してみる

  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 を開き、motion:motionDebug="SHOW_PATH"MotionLayout タグに追加します。

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定義され、アニメーションの開始前と終了後の画面の外観を定義します。デフォルトでは、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 の子であり、遷移中に適用されるすべての KeyFramesKeyPosition など)のセットです。

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 で、keyPositionType によって決定される座標に従って、percentX または percentY だけ移動することにより、motionTarget のパスを変更します。」

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

実際に試す

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

46b179c01801f19e.gif

月は Transition で指定された KeyPosition を通るため、円弧に沿ったことになります。

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

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

そのため、アニメーションの半分で、月は画面上で 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 について

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

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

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

座標系とは

座標系は、空間内の点を指定する手段です。画面上の位置を説明するのにも役立ちます。

MotionLayout 座標系はデカルト座標系です。つまり、X 軸と Y 軸は 2 本の垂直線で定義されます。この 2 つの主な違いは、画面上の X 軸の位置です(Y 軸は常に X 軸と垂直です)。

MotionLayout のすべての座標系では、X 軸と Y 軸の両方で 0.01.0 の範囲の値が使用されます。負の値と 1.0 より大きい値を許可できます。たとえば、percentX の値が -2.0 の場合、X 軸と逆方向に 2 回進むことを意味します。

Algebra のクラスに似すぎているという方は、下の写真を見てください。

parentRelative 座標

a7b7568d46d9dec7.png

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

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

ただし、パスを少し曲げるなど、動きに合わせて相対的にパスを変更する場合は、他の 2 つの座標系のほうが適しています。

deltaRelative 座標

5680bf553627416c.png

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

X 軸は常に画面の水平、Y 軸は常に画面の垂直です。parentRelative との主な違いは、座標はビューが移動する画面の部分のみを示す点です。

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

pathRelative 座標

f3aaadaac8b4a93f.png

MotionLayout の最後の座標系は pathRelative です。他の 2 つとは大きく異なり、X 軸は開始から終了までモーションパスをたどります。したがって、(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 つずつ)を追加します。

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 に進む前に、KeyFrameSet にさらに KeyPositions を追加して、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. 動作中に属性を変更する

ダイナミック アニメーションの構築は、多くの場合、アニメーションの進行に合わせてビューの sizerotation、または alpha を変更することを意味します。MotionLayout では、KeyAttribute を使用して、任意のビューの多くの属性をアニメーション化できます。

このステップでは、KeyAttribute を使用して月の目盛りを作成し、回転させます。また、KeyAttribute を使用して、月の移動がほぼ完了するまでテキストの表示を遅らせます。

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

  1. 前のステップで作成したのと同じアニメーションを含む xml/step5.xml を開きます。多様性を高めるため、この画面では背景として別の宇宙の写真が使用されます。
  2. 月のサイズを大きくして回転させるには、KeyFrameSetkeyFrame="50"keyFrame="100" に 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% に適用されます。50% の最初の KeyAttribute は円弧の上部で発生し、ビューのサイズが 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. クレジットの表示を遅らせるには、alphakeyPosition="85" まで 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. カスタム属性の変更

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

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

このステップでは、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 は、名前に記載されている型のカスタム値です。この例では、カスタム値は指定された色です。

カスタム値の型は次のいずれかです。

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

この 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. コードを使ったランニング モーション

MotionLayoutCoordinatorLayout と併用すると、リッチなアニメーションを作成できます。このステップでは、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 ビューをスクロールするには、motion:minHeightmotion:layout_scrollFlagsMotionLayout に追加します。

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

実際に試す

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

ee5ce4d9e33a59ca.gif

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

12. 完了

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

MotionLayout の実際の使用例については、公式のサンプルをご覧ください。また、必ずドキュメントをご確認ください。

詳細

MotionLayout は、この Codelab で扱っていないその他の機能をサポートしています。たとえば、繰り返しサイクルでパスや属性を制御できる KeyCycle, や、時刻に基づいてアニメーション化できる KeyTimeCycle, などです。それぞれの例については、サンプルをご覧ください。

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