遷移を使用してレイアウト変更をアニメーションにする

Compose を試す
Jetpack Compose は、Android に推奨される UI ツールキットです。Compose でアニメーションを使用する方法を学習します。
<ph type="x-smartling-placeholder"></ph> AnimatedContent →

Android の遷移フレームワークを使用すると、あらゆる種類のモーションを 開始レイアウトと終了レイアウトを提供することで、UI を拡張できます。 ビューをフェードするなど、必要なアニメーションのタイプを ビューサイズの変更などが可能です。また、遷移フレームワークによって、 開始レイアウトから終了レイアウトまでアニメーション化する方法を 確認します

遷移フレームワークには次の機能があります。

  • グループレベルのアニメーション: ビュー階層内のすべてのビューにアニメーション効果を適用する。
  • 組み込みのアニメーション: フェードアウトや動きなどの一般的な効果には、事前定義されたアニメーションを使用します。
  • リソース ファイルのサポート: ビュー階層と組み込みアニメーションをレイアウト リソース ファイルから読み込む。
  • ライフサイクル コールバック: アニメーションと階層を制御するコールバックを受け取る 変更プロセスです

レイアウト変更の合間にアニメーション化するサンプルコードについては、以下をご覧ください。 BasicTransition

2 つのレイアウト間をアニメーション化する基本的な手順は次のとおりです。

  1. Scene オブジェクトを作成し、 調整できます。ただし、開始レイアウトのシーンは 通常は現在のレイアウトから自動的に決定されます
  2. Transition を作成する 使用するアニメーションのタイプを定義します。
  3. 発信 TransitionManager.go() システムはアニメーションを実行してレイアウトを入れ替えます。

図 1 は、レイアウトとファイルの関係を示しています。 アニメーションが完成します

図 1. 基本的なイラスト: アニメーションをどのように作成するかを 定義できます

シーンを作成する

シーンには、すべてのビューとそのビューを含む、ビュー階層の状態が保存されます。 プロパティ値を使用します。遷移フレームワークでは、開始と終了の間にアニメーションを実行できます。 エンディングのシーンです。

レイアウトからシーンを作成できます ビューグループから参照できます。ただし、 多くの場合、遷移の開始シーンは、 現在の UI を表示します。

シーンでは、シーンの変更時に実行する独自のアクションを定義することもできます。 この機能は、設定後にビュー設定を消去するのに便利です。 移動します。

レイアウト リソースからシーンを作成する

Scene インスタンスはレイアウト リソースから直接作成できます 表示されます。この手法は、ファイル内のビュー階層がほぼ静的な場合に使用します。 結果のシーンは、ビュー階層の作成時点のビュー階層の状態を Scene インスタンスを作成しました。ビュー階層を変更すると シーンを再作成します。フレームワークがビュー全体からシーンを作成する 継承されます。レイアウト ファイルの一部からシーンを作成することはできません。

レイアウト リソース ファイルから Scene インスタンスを作成するには、 レイアウトのシーンルートを ViewGroup。次に、 Scene.getSceneForLayout() シーンルートとレイアウト ファイルのリソース ID を にはシーンのビュー階層が含まれます。

シーンのレイアウトを定義する

このセクションの残りのコード スニペットでは、2 つのコードを作成する方法を示します。 同じシーンルート要素を持つ複数の異なるシーンを表現できます。スニペットでは 無関係な複数の Scene オブジェクトを読み込むことができます。 相互に関連しています

この例は、次のレイアウト定義で構成されています。

  • テキストラベルと子があるアクティビティのメイン レイアウト FrameLayout
  • ConstraintLayout: テキスト フィールドが 2 つある最初のシーンです。
  • 同じ 2 つのテキスト フィールドを含む 2 番目のシーンの ConstraintLayout 順序が異なることがあります。

この例は、すべてのアニメーションが子の内部で行われるように設計されています。 アクティビティのメイン レイアウトのレイアウト。メイン レイアウトのテキストラベル 静的に保持されます。

アクティビティのメイン レイアウトは次のように定義されます。

res/layout/activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/master_layout">
    <TextView
        android:id="@+id/title"
        ...
        android:text="Title"/>
    <FrameLayout
        android:id="@+id/scene_root">
        <include layout="@layout/a_scene" />
    </FrameLayout>
</LinearLayout>

このレイアウト定義には、テキスト フィールドと、そのフィールドの子 FrameLayout が含まれています。 あります。最初のシーンのレイアウトは、メイン レイアウト ファイルに含まれています。 これにより、アプリはこれを最初のユーザー インターフェースの一部として表示し、 レイアウト ファイル全体をシーンに読み込むことはできないため、 できます。

最初のシーンのレイアウトは次のように定義されます。

res/layout/a_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    
</androidx.constraintlayout.widget.ConstraintLayout>

2 番目のシーンのレイアウトには同じテキスト フィールドが 2 つあり、 異なる順序で配置されます。次のように定義されます。

res/layout/another_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    
</androidx.constraintlayout.widget.ConstraintLayout>

レイアウトからシーンを作成する

2 つの制約レイアウトの定義を作成すると、 それぞれがシーンになります。これにより、2 つの UI の間で できます。 シーンを取得するには、シーンルートとレイアウトへの参照が必要です。 あります。

次のコード スニペットは、シーンルートへの参照を取得し、 レイアウト ファイルから 2 つの Scene オブジェクトを作成します。

Kotlin

val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this)

Java

Scene aScene;
Scene anotherScene;

// Create the scene root for the scenes in this app.
sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

// Create the scenes.
aScene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this);
anotherScene =
    Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this);

アプリ内には、ビューに基づく 2 つの Scene オブジェクトがあります。 継承されます。どちらのシーンも、 res/layout/activity_main.xmlFrameLayout 要素。

コードでシーンを作成する

また、Terraform からコード内に Scene インスタンスを作成し、 ViewGroup オブジェクト。ビュー階層を変更するときに、この手法を使用します 動的に生成する場合にも使用されます。

コードでビュー階層からシーンを作成するには、 Scene(sceneRoot, viewHierarchy) コンストラクタがあります。このコンストラクタを呼び出すことは、 Scene.getSceneForLayout() この関数を呼び出します。

次のコード スニペットは、Scene の作成方法を示しています。 ビュー階層から作成する必要があります。 コードは次のようになります。

Kotlin

val sceneRoot = someLayoutElement as ViewGroup
val viewHierarchy = someOtherLayoutElement as ViewGroup
val scene: Scene = Scene(sceneRoot, viewHierarchy)

Java

Scene mScene;

// Obtain the scene root element.
sceneRoot = (ViewGroup) someLayoutElement;

// Obtain the view hierarchy to add as a child of
// the scene root when this scene is entered.
viewHierarchy = (ViewGroup) someOtherLayoutElement;

// Create a scene.
mScene = new Scene(sceneRoot, mViewHierarchy);

シーン アクションを作成する

このフレームワークでは、システムが実行時に実行するカスタム シーン アクションを定義できます。 シーンに入る、または終了する多くの場合、カスタム シーン アクションの定義は フレームワークがシーン間の変更をアニメーション化するので、不要である 自動的に適用されます。

シーン アクションは、次のような場合に便利です。

  • 同じ階層にないビューをアニメーション化する。ビューをアニメーション化できます。 終了シーンと開始シーンのアクションを使って 開始シーンと終了シーンを表現します
  • 遷移フレームワークで自動的にアニメーション化できないビューをアニメーション化するには、 たとえば ListView オブジェクトなどです。詳細 詳しくは、制限事項のセクションをご覧ください。

カスタム シーン アクションを提供するには、アクションを次のように定義します。 Runnable オブジェクトを作成し、 Scene.setExitAction() または Scene.setEnterAction() 使用できます。フレームワークは起動時に setExitAction() 関数を呼び出します。 シーケンスを描画して、遷移アニメーションと setEnterAction() を実行する前に、 関数を遷移アニメーションの実行後に終了シーンに追加します。

遷移を適用する

遷移フレームワークは、シーン間のアニメーションのスタイルを Transition オブジェクト。Transition をインスタンス化するには、組み込みの などのサブクラスも AutoTransition および Fade、または 独自の移行を定義するをご覧ください。 その後、 終了 Scene を渡してシーン間のアニメーションを と TransitionTransitionManager.go()

遷移ライフサイクルはアクティビティのライフサイクルと似ており、 フレームワークが監視する遷移状態は、開始から開始までの間に アニメーションが終了したときなどです重要なライフサイクル状態では、フレームワークは ユーザー インターフェースを調整するために実装できるコールバック関数を 移行の段階ごとに

遷移を作成する

前のセクションでは、VM の状態を表すシーンを作成する方法を ビュー階層を作成できます開始シーンと終了シーンを定義したら、 アニメーションを変更する場合は、アニメーションを定義する Transition オブジェクトを作成します。 フレームワークでは、リソース ファイルに組み込みの遷移を指定することも、 コード内でインフレートしたり、組み込みの遷移の 使用できます。

表 1. 組み込みの遷移タイプ。

クラス タグ 効果
AutoTransition <autoTransition/> デフォルトの遷移。ビューのフェードアウト、移動、サイズ変更、フェードインがこの順序で行われます。
ChangeBounds <changeBounds/> ビューの移動とサイズ変更を行います。
ChangeClipBounds <changeClipBounds/> シーンの前後の View.getClipBounds() をキャプチャします 遷移中に変化をアニメーション表示します。
ChangeImageTransform <changeImageTransform/> シーンの前後の ImageView のマトリックスをキャプチャします。 遷移中にアニメーション化できます。
ChangeScroll <changeScroll/> シーンの前後のターゲットのスクロール プロパティを取得します 変更がアニメーション化されます。
ChangeTransform <changeTransform/> シーンの変更前と変更後のビューの縮尺と回転をキャプチャします 遷移中に変化をアニメーション表示します。
Explode <explode/> 開始時と終了時にターゲット ビューの表示設定の変化をトラッキングします ビューをシーンの端から出入りします。
Fade <fade/> fade_in はビューをフェードインします。
fade_out はビューをフェードアウトします。
fade_in_out(デフォルト)は、fade_out の後に続けて実行します。 fade_in
Slide <slide/> 開始時と終了時のターゲット ビューの公開設定の変更を追跡します ビューをシーンの端から出入りできます。

リソース ファイルから遷移インスタンスを作成する

この手法を使用すると、 できます。この手法は、複雑なクエリを アプリケーション コードから移行定義を定義します。 詳しくは、複数の遷移の指定をご覧ください。

リソース ファイルに組み込みの遷移を指定する方法は次のとおりです。

  • res/transition/ ディレクトリをプロジェクトに追加します。
  • このディレクトリ内に新しい XML リソース ファイルを作成します。
  • 組み込みの遷移のいずれかに XML ノードを追加します。

たとえば、次のリソース ファイルでは Fade 遷移を指定しています。

res/transition/fade_transition.xml

<fade xmlns:android="http://schemas.android.com/apk/res/android" />

次のコード スニペットは、内部で Transition インスタンスをインフレートする方法を示しています。 次のようにリソース ファイルからアクティビティを取得します。

Kotlin

var fadeTransition: Transition =
    TransitionInflater.from(this)
                      .inflateTransition(R.transition.fade_transition)

Java

Transition fadeTransition =
        TransitionInflater.from(this).
        inflateTransition(R.transition.fade_transition);

コードで遷移インスタンスを作成する

この手法は、次のような場合に遷移オブジェクトを動的に作成する場合に便利です。 コード内のユーザー インターフェースを変更し、組み込みの簡単な ほとんどまたはまったくないインスタンスです。

組み込みの遷移のインスタンスを作成するには、 Transition クラスのサブクラス内のコンストラクタを使用します。たとえば、 次のコード スニペットは、Fade 遷移のインスタンスを作成します。

Kotlin

var fadeTransition: Transition = Fade()

Java

Transition fadeTransition = new Fade();

遷移を適用する

通常は、ビュー階層間の変更遷移を適用します。 イベントに応答することもできます。たとえば、次のような検索アプリについて考えてみましょう。 ユーザーが検索キーワードを入力して検索ボタンをタップすると、アプリが切り替わります。 結果レイアウトを表すシーンに適用しながら、遷移効果を 検索ボタンがフェードアウトし、検索結果がフェードインします。

イベントに応じて遷移を適用する際にシーンを変更するには、 アクティビティに対して、末尾で TransitionManager.go() クラス関数を呼び出します。 アニメーションに使用する移行インスタンスを 次のスニペット:

Kotlin

TransitionManager.go(endingScene, fadeTransition)

Java

TransitionManager.go(endingScene, fadeTransition);

フレームワークは、ビューでシーンルート内のビュー階層を変更します。 で指定されたアニメーションの実行中に終了シーンから階層を 使用されます。開始シーンは最後にある終了シーンです 説明します。前に遷移がない場合は、開始シーンが決定されます。 ユーザー インターフェースの現在の状態から自動的に移行されます。

移行インスタンスを指定しない場合、移行マネージャーは 自動移行によって、ほとんどの状況で妥当な処理が行われます。対象 詳細については、API リファレンスの TransitionManager クラスです。

特定のターゲット ビューを選択する

フレームワークは、開始シーンと終了シーンのすべてのビューに遷移を適用します。 できます。場合によっては、アニメーションをサブセットにのみ適用したいことがあります。 ビューの数を表します。このフレームワークでは、使用するビューを選択して、 アニメーション化できます。たとえば、フレームワークでは、変更のアニメーションは ListView オブジェクトなので、遷移中にアニメーション化しないでください。

遷移によってアニメーション化される各ビューをターゲットと呼びます。使用できるのは、 シーンに関連付けられたビュー階層の一部であるターゲットを選択します。

ターゲットのリストから 1 つ以上のビューを削除するには、 removeTarget() メソッドを呼び出します。指定したビューのみを ターゲットのリストにある addTarget() 使用します。詳しくは、API リファレンスの Transition クラス。

複数の遷移を指定する

アニメーションの効果を最大限に引き出すには、変化の種類に合わせてアニメーションを調整しましょう。 発生しませんたとえば、いくつかのビューを削除して、 フェードアウトまたはフェードイン アニメーションを使用すると、 これは、一部のビューが使用できなくなったことを示します。ビューを移動する場合 移動にアニメーションを付けると、 ユーザーはビューの新しい位置に気づきます。

遷移フレームワークがアニメーションなので、アニメーションを 1 つだけ選択する必要はありません。 を使用すると、一連のアニメーションを含む遷移セットでアニメーション効果を組み合わせることができます。 個別の組み込みまたはカスタムの遷移を作成します

XML の遷移の集合から遷移セットを定義するには、 res/transitions/ ディレクトリ内にリソース ファイルを作成し、その下に遷移をリストします。 TransitionSet 要素。たとえば、次のスニペットは、 AutoTransition と同じ動作を持つ遷移セットを指定する クラス:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
    <fade android:fadingMode="fade_in" />
</transitionSet>

遷移セットをインフレートするには、 TransitionSet オブジェクト コード内で TransitionInflater.from() おすすめします。TransitionSet クラスは、 Transition ので、他のツールと同じように移行マネージャーと一緒に使用できます 他の Transition インスタンス。

シーンを使用せずに遷移を適用する

ユーザー インターフェースを変更する方法は、ビュー階層の変更だけではありません。マイページ ビュー内の子ビューを追加、変更、削除して、変更を加えることもできます。 現在の階層を定義します。

たとえば、Google Chat のメッセージ機能を使用した 作成します。検索入力フィールドと検索を表示するレイアウトから始める アイコンをクリックします。結果を表示するようにユーザー インターフェースを変更するには、検索ボタンを削除します ユーザーがタップしたときに ViewGroup.removeView() 呼び出して検索結果を追加します。 ViewGroup.addView() 使用します。

別の方法として、2 つの階層構造があり 2 つの階層に ほぼ同じです2 つの個別のレイアウト ファイルを作成して維持するのではなく、 ユーザー インターフェースを少し変えますが、1 つのレイアウト ファイルは ビュー階層を格納できます。

この方法で現在のビュー階層内で変更を加えると、 シーンを作成する必要があります代わりに、境界内に遷移を作成して適用することで、 遅延遷移を使用するビュー階層の 2 つの状態。この機能の 遷移フレームワークは現在のビュー階層の状態から始まり、 そのビューに加えた変更が適用され、そのビューに加えた変更を システムがユーザー インターフェースを再描画するときに変更されます。

単一のビュー階層内に遅延遷移を作成する手順は次のとおりです。 手順:

  1. 遷移をトリガーするイベントが発生したときに、 TransitionManager.beginDelayedTransition() すべてのビューの親ビューを提供します 使用するトランジションも 指定できますフレームワークには、 子ビューの状態とそのプロパティ値。
  2. ユースケースの必要性に応じて、子ビューを変更します。フレームワーク には、子ビューとそのプロパティに加えた変更が記録されます。
  3. 変更に応じてユーザー インターフェースが再描画されると、 フレームワークは、元の状態と新しい状態の間の変化をアニメーション化します。

次の例は、ビューへのテキストビューの追加をアニメーション化する方法を示しています。 階層構造を表現します。最初のスニペットは、 定義ファイル:

res/layout/activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <EditText
        android:id="@+id/inputText"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    ...
</androidx.constraintlayout.widget.ConstraintLayout>

次のスニペットは、テキストビューの追加をアニメーション化するコードを示しています。

MainActivity

Kotlin

setContentView(R.layout.activity_main)
val labelText = TextView(this).apply {
    text = "Label"
    id = R.id.text
}
val rootView: ViewGroup = findViewById(R.id.mainLayout)
val mFade: Fade = Fade(Fade.IN)
TransitionManager.beginDelayedTransition(rootView, mFade)
rootView.addView(labelText)

Java

private TextView labelText;
private Fade mFade;
private ViewGroup rootView;
...
// Load the layout.
setContentView(R.layout.activity_main);
...
// Create a new TextView and set some View properties.
labelText = new TextView(this);
labelText.setText("Label");
labelText.setId(R.id.text);

// Get the root view and create a transition.
rootView = (ViewGroup) findViewById(R.id.mainLayout);
mFade = new Fade(Fade.IN);

// Start recording changes to the view hierarchy.
TransitionManager.beginDelayedTransition(rootView, mFade);

// Add the new TextView to the view hierarchy.
rootView.addView(labelText);

// When the system redraws the screen to show this update,
// the framework animates the addition as a fade in.

遷移ライフサイクルのコールバックを定義する

遷移ライフサイクルは、アクティビティ ライフサイクルと似ています。これは、 呼び出しと呼び出しの間の期間中にフレームワークが監視する遷移状態 TransitionManager.go() 関数に渡され、 追加します。重要なライフサイクル状態では、フレームワークは TransitionListener で定義 行うことができます。

遷移ライフサイクル コールバックは、たとえばビューのコピーや 開始ビュー階層から終了ビュー階層までのプロパティ値 必要があります。単純に最初のビューから値をコピーして 最終的なビュー階層内のビューも考慮されません。最終ビュー階層は完全に 自動的に増大します代わりに、値を保存しておかなければならない 終了ビュー階層にコピーして、フレームワークが 移行が完了しました。移行が完了したときに通知を受け取るには、 実装する TransitionListener.onTransitionEnd() おすすめします。

詳しくは、API リファレンスの TransitionListener クラスです。

制限事項

このセクションでは、遷移フレームワークに関する既知の制限事項をいくつか示します。

  • 適用されたアニメーション SurfaceView が表示されないことがあります 確認します。SurfaceView インスタンスは UI 以外のスレッドから更新されるため、 更新が他のビューのアニメーションと同期していない場合があります。
  • 切り替え効果の種類によっては、希望するアニメーション効果が得られない場合がある TextureView に適用した場合。
  • 拡張するクラス AdapterViewListView など) お子様のビューの管理方法 説明しましたAdapterView に基づいてビューをアニメーション化しようとすると、 デバイスのディスプレイが応答しなくなることがあります。
  • 次のように TextView のサイズを変更しようとすると、 アニメーションでは、オブジェクトが完全に移動する前にテキストが新しい位置にポップされます。 サイズ変更されます。この問題を回避するには、 あります。