プロパティ アニメーションの概要

Compose を試す
Jetpack Compose は、Android に推奨される UI ツールキットです。Compose でアニメーションを使用する方法を学習します。

プロパティ アニメーション システムは、ほぼすべてのアニメーション化を可能にする堅牢なフレームワークです。画面に描画されるかどうかに関係なく、オブジェクトのプロパティを経時的に変化するようにアニメーションを定義できます。プロパティ アニメーションは、指定された時間にわたってプロパティ(オブジェクト内のフィールド)値を変更します。何かをアニメーション化するには、アニメーション化するオブジェクト プロパティ(画面上のオブジェクトの位置、アニメーションの長さ、アニメーション化する値など)を指定します。

プロパティ アニメーション システムを使用すると、アニメーションの次の特性を定義できます。

  • 継続時間: アニメーションの長さを指定できます。デフォルトの長さは 300 ms です。
  • 時間補間: プロパティの値をアニメーションの現在の経過時間の関数として計算する方法を指定できます。
  • 繰り返しの回数と動作: ある時間が終了したときにアニメーションを繰り返すかどうかと、アニメーションを繰り返す回数を指定できます。アニメーションを逆方向に再生するかどうかを指定することもできます。「戻る」に設定すると、繰り返しの回数に達するまで、アニメーションが前後に繰り返し再生されます。
  • アニメーター セット: アニメーションを、一緒に、連続して、または指定した遅延後に再生する論理セットにグループ化できます。
  • フレーム更新遅延: アニメーションのフレームを更新する頻度を指定できます。デフォルトでは 10 ミリ秒ごとに更新されるように設定されていますが、アプリケーションがフレームを更新できる速度は、最終的にはシステム全体のビジー状態と、基になるタイマーをシステムが処理する速度に依存します。

プロパティ アニメーションの完全な例については、GitHub の CustomTransition サンプルの ChangeColor クラスをご覧ください。

プロパティ アニメーションの仕組み

まず、簡単な例を使用してアニメーションの仕組みについて説明します。図 1 は、画面上の水平位置を表す x プロパティでアニメーション化された架空のオブジェクトを示しています。アニメーションの継続時間は 40 ms に設定し、移動距離は 40 ピクセルに設定します。10 ミリ秒(デフォルトのフレーム リフレッシュ レート)ごとに、オブジェクトが水平方向に 10 ピクセルずつ移動します。40 ミリ秒が経過するとアニメーションが停止し、オブジェクトは水平位置 40 で終了します。これは、線形補間を使用したアニメーションの例です。これは、オブジェクトが一定の速度で移動することを意味します。

図 1. 線形アニメーションの例

非線形補間を行うアニメーションを指定することもできます。図 2 は、アニメーションの開始時に加速し、アニメーションの終了時に減速する架空のオブジェクトを示しています。オブジェクトは 40 ms で 40 ピクセル移動しますが、非線形です。このアニメーションは最初は中間点まで加速し、その後中間点から終了まで減速します。図 2 に示すように、アニメーションの開始時と終了時の移動距離は、中央の場合よりも短くなっています。

図 2. 非線形アニメーションの例

上記のようなアニメーションが、プロパティ アニメーション システムの重要なコンポーネントによってどのように計算されるかを詳しく見てみましょう。図 3 は、メインクラスが互いにどのように連携するかを示しています。

図 3. アニメーションの計算方法

ValueAnimator オブジェクトは、アニメーションの実行時間やアニメーション化するプロパティの現在の値など、アニメーションのタイミングを追跡します。

ValueAnimator は、アニメーションの補間を定義する TimeInterpolator と、アニメーション化するプロパティの値の計算方法を定義する TypeEvaluator をカプセル化します。たとえば図 2 では、使用される TimeInterpolatorAccelerateDecelerateInterpolatorTypeEvaluatorIntEvaluator になります。

アニメーションを開始するには、ValueAnimator を作成し、アニメーション化するプロパティの開始値と終了値、アニメーションの継続時間を指定します。start() を呼び出すと、アニメーションが開始されます。アニメーション全体を通して、ValueAnimator はアニメーションの継続時間と経過時間に基づいて、0 ~ 1 の範囲の経過時間の割合を計算します。経過時間の割合は、アニメーションが完了した時間の割合を表し、0 は 0%、1 は 100% を表します。たとえば図 1 では、合計時間が t = 40 ms であるため、t = 10 ms での経過した割合は 0.25 になります。

ValueAnimator は、経過した割合の計算を完了すると、現在設定されている TimeInterpolator を呼び出して、補間された割合を計算します。補間された割合は、設定された時間補間を考慮した新しい割合に経過した割合をマッピングします。たとえば図 2 では、アニメーションがゆっくりと加速するため、補間された割合(約 0.15)は、t = 10 ミリ秒での経過時間(0.25)よりも小さくなります。図 1 では、補間された割合は常に経過した割合と同じです。

補間された割合が計算されると、ValueAnimator は適切な TypeEvaluator を呼び出し、アニメーションの補間された割合、開始値、アニメーションの終了値に基づいて、アニメーション化するプロパティの値を計算します。たとえば、図 2 では、t = 10 ms で補間された割合が 0.15 であるため、その時点でのプロパティの値は 0.15 × (40 - 0)、つまり 6 になります。

プロパティ アニメーションとビュー アニメーションの違い

ビュー アニメーション システムには、View オブジェクトのみをアニメーション化する機能があるため、View 以外のオブジェクトをアニメーション化する場合は、独自のコードを実装する必要があります。また、ビュー アニメーション システムには、アニメーション化する View オブジェクトのいくつかの要素のみを公開するという制約もあります。たとえば、ビューのスケーリングと回転は公開され、背景色は公開されません。

ビュー アニメーション システムのもう一つの欠点は、ビューが描画された場所のみが変更され、実際のビュー自体は変更されないことです。たとえば、画面上を移動するボタンをアニメーション化した場合、ボタンは正しく描画されますが、ボタンをクリックできる実際の位置は変わりません。そのため、これを処理するための独自のロジックを実装する必要があります。

プロパティ アニメーション システムでは、これらの制約が完全に削除され、任意のオブジェクト(ビューと非ビュー)の任意のプロパティをアニメーション化でき、オブジェクト自体を実際に変更できます。プロパティ アニメーション システムは、アニメーションの実行方法も堅牢です。大まかに言うと、色、位置、サイズなどのアニメーション化するプロパティにアニメーターを割り当て、複数のアニメーターの補間や同期などのアニメーションの要素を定義できます。

一方、ビュー アニメーション システムでは、セットアップにかかる時間が短くなり、記述するコードも少なくて済みます。ビュー アニメーションで必要な作業がすべて完了している場合、または既存のコードがすでに意図したとおりに動作している場合は、プロパティ アニメーション システムを使用する必要はありません。また、ユースケースが発生した場合は、それぞれの状況で両方のアニメーション システムを使用するのが合理的です。

API の概要

プロパティ アニメーション システムの API のほとんどは、android.animation にあります。ビュー アニメーション システムでは、android.view.animation ですでに多くの interpolator が定義されているため、プロパティ アニメーション システムでもそれらの interpolator を使用できます。次の表に、プロパティ アニメーション システムの主なコンポーネントを示します。

Animator クラスは、アニメーションを作成するための基本構造を提供します。このクラスは、値のアニメーション化を完全にサポートするために拡張する必要がある最小限の機能のみを提供するため、通常は直接使用しません。次のサブクラスは Animator を拡張します。

表 1. アニメーター

クラス 説明
ValueAnimator プロパティ アニメーション用のメインのタイミング エンジン。アニメーション化するプロパティの値も計算します。アニメーション値を計算し、各アニメーションのタイミングの詳細、アニメーションを繰り返すかどうかに関する情報、更新イベントを受信するリスナー、評価するカスタムタイプを設定する機能など、すべてのコア機能を備えています。プロパティのアニメーション化には、アニメーション化する値の計算と、アニメーション化するオブジェクトとプロパティへのそれらの値の設定の 2 つの部分があります。ValueAnimator は 2 番目の部分を実行しないため、ValueAnimator によって計算された値の更新をリッスンし、独自のロジックでアニメーション化するオブジェクトを変更する必要があります。詳しくは、ValueAnimator を使用したアニメーション化のセクションをご覧ください。
ObjectAnimator アニメーション化するターゲット オブジェクトとオブジェクト プロパティを設定できる ValueAnimator のサブクラス。このクラスは、アニメーションの新しい値を計算するときに、それに応じてプロパティを更新します。ターゲット オブジェクトの値をアニメーション化するプロセスが非常に簡単になるため、ほとんどの場合は ObjectAnimator を使用します。ただし、ObjectAnimator には制限(ターゲット オブジェクトに特定のアクセサ メソッドが必要など)があるため、ValueAnimator を直接使用したい場合もあります。
AnimatorSet アニメーションが互いに関連して実行されるようにグループ化するメカニズムを提供します。アニメーションは、一緒に、連続して、または指定した遅延後に再生するよう設定できます。詳しくは、Animator Set を使用した複数のアニメーションの振付のセクションをご覧ください。

評価担当者は、特定のプロパティの値の計算方法をプロパティ アニメーション システムに指示します。Animator クラスから提供されるタイミング データ、アニメーションの開始値と終了値を取得し、このデータに基づいてプロパティのアニメーション値を計算します。プロパティ アニメーション システムには、次のエバリュエータが用意されています。

表 2. エバリュエータ

クラス / インターフェース 説明
IntEvaluator int プロパティの値を計算するデフォルトのエバリュエータ。
FloatEvaluator float プロパティの値を計算するデフォルトのエバリュエータ。
ArgbEvaluator 16 進数の値として表されるカラー プロパティの値を計算するデフォルトのエバリュエータ。
TypeEvaluator 独自のエバリュエータを作成できるインターフェース。intfloat、色以外のオブジェクト プロパティをアニメーション化する場合は、TypeEvaluator インターフェースを実装して、そのオブジェクト プロパティのアニメーション値の計算方法を指定する必要があります。また、intfloat、色の値にカスタムの TypeEvaluator を指定して、これらの型をデフォルトの動作とは異なる方法で処理することもできます。カスタム エバリュエータの作成方法の詳細については、TypeEvaluator を使用するをご覧ください。

時間補間器は、アニメーション内の特定の値を時間の関数として計算する方法を定義します。たとえば、アニメーションがアニメーション全体にわたって線形的に発生する(つまり、アニメーションが時間を通して均等に動く)ように指定したり、非線形の時間を使用するように指定したりできます(アニメーションの開始時に加速し、終了時に減速するなど)。表 3 に、android.view.animation に含まれるインターポレータを示します。提供されている interpolator がニーズに合わない場合は、TimeInterpolator インターフェースを実装して独自の interpolator を作成します。カスタム interpolator の記述方法について詳しくは、interpolator を使用するをご覧ください。

表 3: interpolator

クラス / インターフェース 説明
AccelerateDecelerateInterpolator 変化の速度の開始と終了はゆっくりですが、途中で加速する interpolator。
AccelerateInterpolator 変化の速度がゆっくりと開始してから加速する interpolator。
AnticipateInterpolator 変化が逆方向に開始してから、順方向に切り替わるインターポレータ。
AnticipateOvershootInterpolator 変化が逆方向から始まり、フリングして前進し、ターゲット値を過ぎてから、最終的に最終値に戻る interpolator。
BounceInterpolator 変化が最後にバウンドするインターポレータ。
CycleInterpolator 指定したサイクル数だけアニメーションが繰り返されるインターポレータ。
DecelerateInterpolator 変化の速度が速く開始してから減速する interpolator。
LinearInterpolator 変化の速度が一定であるインターポレータ。
OvershootInterpolator 変化がはみ出し、最後の値を過ぎてから逆戻りする interpolator。
TimeInterpolator 独自のインターポレータを実装できるインターフェース。

ValueAnimator を使用してアニメーション化する

ValueAnimator クラスを使用すると、アニメーション化する intfloat、または色の値のセットを指定することで、アニメーションの全期間にわたって特定の型の値をアニメーション化できます。ValueAnimator を取得するには、そのファクトリ メソッド(ofInt()ofFloat()ofObject())のいずれかを呼び出します。次に例を示します。

Kotlin

ValueAnimator.ofFloat(0f, 100f).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();

このコードでは、start() メソッドが実行されると、ValueAnimator が 1,000 ミリ秒にわたって 0 ~ 100 の間のアニメーションの値の計算を開始します。

次のようにして、アニメーション化するカスタムタイプを指定することもできます。

Kotlin

ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

このコードでは、start() メソッドの実行時に、ValueAnimatorMyTypeEvaluator から提供されるロジックを使用して、startPropertyValue から endPropertyValue までのアニメーションの値の計算を開始します(1,000 ms)。

アニメーションの値を使用するには、次のコードに示すように、AnimatorUpdateListenerValueAnimator オブジェクトに追加します。

Kotlin

ValueAnimator.ofObject(...).apply {
    ...
    addUpdateListener { updatedAnimation ->
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        textView.translationX = updatedAnimation.animatedValue as Float
    }
    ...
}

Java

animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

onAnimationUpdate() メソッドで、更新されたアニメーション値にアクセスし、それをいずれかのビューのプロパティで使用できます。リスナーについて詳しくは、アニメーション リスナーに関するセクションをご覧ください。

ObjectAnimator を使用してアニメーション化する

ObjectAnimatorValueAnimator のサブクラス(前のセクションで説明)であり、ValueAnimator のタイミング エンジンおよび値計算と、ターゲット オブジェクトの名前付きプロパティをアニメーション化する機能を組み合わせます。これにより、アニメーション化されたプロパティが自動的に更新されるため、ValueAnimator.AnimatorUpdateListener を実装する必要がなくなり、オブジェクトのアニメーション化がはるかに簡単になります。

ObjectAnimator のインスタンス化は ValueAnimator に似ていますが、オブジェクトとそのオブジェクトのプロパティ名(文字列として)、アニメーション化する値も指定します。

Kotlin

ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
    duration = 1000
    start()
}

Java

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

ObjectAnimator でプロパティを正しく更新するには、次のことを行う必要があります。

  • アニメーション化するオブジェクト プロパティには、set<PropertyName>() 形式のセッター関数(キャメルケース)が必要です。ObjectAnimator はアニメーション中にプロパティを自動的に更新するため、このセッター メソッドでプロパティにアクセスできる必要があります。たとえば、プロパティ名が foo の場合は、setFoo() メソッドが必要です。このセッター メソッドが存在しない場合は、次の 3 つの選択肢があります。
    • セッター メソッドをクラスに追加する権限がある場合は、追加する。
    • 変更権限のあるラッパークラスを使用し、そのラッパーが有効なセッター メソッドを含む値を受け取って元のオブジェクトに転送するようにします。
    • 代わりに ValueAnimator を使用する。
  • ObjectAnimator ファクトリ メソッドのいずれかで values... パラメータの値を 1 つだけ指定した場合、それがアニメーションの終了値とみなされます。したがって、アニメーション化するオブジェクト プロパティには、アニメーションの開始値を取得するゲッター関数が必要です。ゲッター関数は、get<PropertyName>() の形式にする必要があります。たとえば、プロパティ名が foo の場合は、getFoo() メソッドが必要です。
  • アニメーション化するプロパティのゲッター メソッド(必要な場合)とセッター メソッドは、ObjectAnimator に指定する開始値と終了値と同じ型で動作する必要があります。たとえば、次の ObjectAnimator を作成する場合は、targetObject.setPropName(float)targetObject.getPropName() が必要です。
    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    
  • アニメーション化するプロパティまたはオブジェクトによっては、View の invalidate() メソッドを呼び出して、更新されたアニメーション化された値を使用して画面を強制的に再描画する必要があります。これは onAnimationUpdate() コールバックで行います。たとえば、ドローアブル オブジェクトの color プロパティをアニメーション化すると、そのオブジェクトが再描画されたときにのみ画面が更新されます。setAlpha()setTranslationX() など、View のすべてのプロパティ セッターは View を適切に無効化するため、新しい値を使用してこれらのメソッドを呼び出すときに View を無効にする必要はありません。リスナーの詳細については、アニメーション リスナーに関するセクションをご覧ください。

AnimatorSet を使用して複数のアニメーションを演出する

多くの場合、別のアニメーションの開始または終了のタイミングに依存するアニメーションを再生する必要があります。Android システムでは、アニメーションを AnimatorSet にバンドルできるため、アニメーションを同時に開始するか、連続的に開始するか、指定した遅延後に開始するかを指定できます。AnimatorSet オブジェクトを相互にネストすることもできます。

次のコード スニペットは、次のように Animator オブジェクトを再生します。

  1. bounceAnim を再生する。
  2. squashAnim1squashAnim2stretchAnim1stretchAnim2 を同時に再生します。
  3. bounceBackAnim を再生する。
  4. fadeAnim を再生する。

Kotlin

val bouncer = AnimatorSet().apply {
    play(bounceAnim).before(squashAnim1)
    play(squashAnim1).with(squashAnim2)
    play(squashAnim1).with(stretchAnim1)
    play(squashAnim1).with(stretchAnim2)
    play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
}
AnimatorSet().apply {
    play(bouncer).before(fadeAnim)
    start()
}

Java

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();

アニメーション リスナー

以下で説明するリスナーを使用して、アニメーションの期間中に重要なイベントをリッスンできます。

  • Animator.AnimatorListener
    • onAnimationStart() - アニメーションの開始時に呼び出されます。
    • onAnimationEnd() - アニメーションの終了時に呼び出されます。
    • onAnimationRepeat() - アニメーションが繰り返されるときに呼び出されます。
    • onAnimationCancel() - アニメーションがキャンセルされたときに呼び出されます。キャンセルされたアニメーションは、終了方法に関係なく、onAnimationEnd() も呼び出します。
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate() - アニメーションのすべてのフレームで呼び出されます。アニメーション中に ValueAnimator によって生成された計算値を使用するために、このイベントをリッスンします。値を使用するには、イベントに渡された ValueAnimator オブジェクトをクエリし、getAnimatedValue() メソッドで現在のアニメーション化された値を取得します。ValueAnimator を使用する場合は、このリスナーを実装する必要があります。

      アニメーション化するプロパティまたはオブジェクトによっては、ビューで invalidate() を呼び出して、画面のその領域を新しいアニメーション化値で強制的に再描画する必要があります。たとえば、ドローアブル オブジェクトの color プロパティをアニメーション化すると、オブジェクトが再描画されたときにのみ、画面が更新されます。setAlpha()setTranslationX() など、View のすべてのプロパティ セッターは View を適切に無効化するため、新しい値を使用してこれらのメソッドを呼び出すときに View を無効にする必要はありません。

Animator.AnimatorListener インターフェースのすべてのメソッドを実装しない場合は、Animator.AnimatorListener インターフェースを実装する代わりに AnimatorListenerAdapter クラスを拡張できます。AnimatorListenerAdapter クラスには、オーバーライド可能なメソッドの空の実装が用意されています。

たとえば、次のコード スニペットは、onAnimationEnd() コールバックに対してのみ AnimatorListenerAdapter を作成します。

Kotlin

ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            balls.remove((animation as ObjectAnimator).target)
        }
    })
}

Java

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
    balls.remove(((ObjectAnimator)animation).getTarget());
}

ViewGroup オブジェクトへのレイアウト変更をアニメーション化する

プロパティ アニメーション システムには、ViewGroup オブジェクトの変更をアニメーション化する機能だけでなく、View オブジェクト自体をアニメーション化する機能も用意されています。

LayoutTransition クラスを使用すると、ViewGroup 内のレイアウト変更をアニメーション化できます。ViewGroup 内のビューは、ViewGroup に追加したり ViewGroup から削除したり、VISIBLEINVISIBLEGONE を使用してビューの setVisibility() メソッドを呼び出すときに、表示と非表示のアニメーションで処理される場合があります。ViewGroup 内の残りの View は、View を追加または削除すると、新しい位置にアニメーション化することもできます。LayoutTransition オブジェクトで以下のアニメーションを定義するには、setAnimator() を呼び出し、次のいずれかの LayoutTransition 定数を含む Animator オブジェクトを渡します。

  • APPEARING - コンテナ内に表示されているアイテムで実行されるアニメーションを示すフラグ。
  • CHANGE_APPEARING - コンテナに新しいアイテムが表示されることで変更されるアイテムで実行されるアニメーションを示すフラグ。
  • DISAPPEARING - コンテナから消えるアイテムで実行されるアニメーションを示すフラグ。
  • CHANGE_DISAPPEARING - アイテムがコンテナから消えることで変化するアイテムで実行されるアニメーションを示すフラグ。

この 4 種類のイベントに対して独自のカスタム アニメーションを定義して、レイアウト遷移の外観をカスタマイズできます。また、デフォルトのアニメーションを使用するようアニメーション システムに伝えることもできます。

ViewGroup の android:animateLayoutchanges 属性を true に設定するには、次の操作を行います。

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

この属性を true に設定すると、ViewGroup に対して追加または削除されたビューや、ViewGroup 内の残りのビューが自動的にアニメーション化されます。

StateListAnimator を使用してビューの状態の変化をアニメーション化する

StateListAnimator クラスを使用すると、ビューの状態が変化したときに実行されるアニメーターを定義できます。このオブジェクトは Animator オブジェクトのラッパーとして動作し、指定されたビュー状態(「押下」や「フォーカス」など)が変更されるたびにアニメーションを呼び出します。

XML リソースで StateListAnimator を定義するには、ルート <selector> 要素と子要素 <item> を使用し、各要素で StateListAnimator クラスで定義された異なるビュー状態を指定します。各 <item> には、プロパティ アニメーション セットの定義が含まれています。

たとえば、次のファイルでは、ビューが押されたときにビューの x スケールと y スケールを変更する状態リスト アニメーターを作成します。

res/xml/animate_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 150% -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100% -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

状態リスト アニメーターをビューにアタッチするには、次のように android:stateListAnimator 属性を追加します。

<Button android:stateListAnimator="@xml/animate_scale"
        ... />

これで、このボタンの状態が変化したときに、animate_scale.xml で定義されたアニメーションが使用されるようになりました。

または、コード内のビューに状態リスト アニメーターを割り当てるには、AnimatorInflater.loadStateListAnimator() メソッドを使用して、View.setStateListAnimator() メソッドでビューにアニメーターを割り当てます。

または、ビューのプロパティをアニメーション化する代わりに、AnimatedStateListDrawable を使用して、状態変化の合間にドローアブル アニメーションを再生することもできます。Android 5.0 の一部のシステム ウィジェットでは、デフォルトでこれらのアニメーションを使用しています。次の例は、AnimatedStateListDrawable を XML リソースとして定義する方法を示しています。

<!-- res/drawable/myanimstatedrawable.xml -->
<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- provide a different drawable for each state-->
    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
        android:state_pressed="true"/>
    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
        android:state_focused="true"/>
    <item android:id="@id/default"
        android:drawable="@drawable/drawableD"/>

    <!-- specify a transition -->
    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="15" android:drawable="@drawable/dt1"/>
            <item android:duration="15" android:drawable="@drawable/dt2"/>
            ...
        </animation-list>
    </transition>
    ...
</animated-selector>

TypeEvaluator を使用する

Android システムで認識されていないタイプをアニメーション化する場合は、TypeEvaluator インターフェースを実装して、独自のエバリュエータを作成できます。Android システムが認識する型は intfloat、または色であり、IntEvaluatorFloatEvaluatorArgbEvaluator 型エバリュエータでサポートされています。

TypeEvaluator インターフェースに実装するメソッドは、evaluate() メソッドのみです。これにより、使用しているアニメーターは、アニメーションの現在の時点のアニメーション プロパティに適切な値を返すことができます。FloatEvaluator クラスは、これを行う方法を示しています。

Kotlin

private class FloatEvaluator : TypeEvaluator<Any> {

    override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
        return (startValue as Number).toFloat().let { startFloat ->
            startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
        }
    }

}

Java

public class FloatEvaluator implements TypeEvaluator {

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

注: ValueAnimator(または ObjectAnimator)を実行すると、アニメーションの現在の経過時間の割合(0 ~ 1 の値)が計算され、使用している補間器に応じて補間バージョンが計算されます。補間された小数は、TypeEvaluatorfraction パラメータを通じて受け取るものであるため、アニメーション化された値を計算するときに interpolator を考慮する必要はありません。

インターポレータを使用する

interpolator は、アニメーション内の特定の値を時間の関数として計算する方法を定義します。たとえば、アニメーションがアニメーション全体にわたって線形的に発生する(つまり、アニメーションが時間を通して均等に移動する)ように指定できます。また、非線形の時間(たとえば、アニメーションの開始時または終了時に加速または減速を使用する)を使用するようにアニメーションを指定することもできます。

アニメーション システムの interpolator は、Animator からアニメーションの経過時間を表す小数を受け取ります。Interpolator は、目的のアニメーションのタイプに合わせてこの割合を変更します。Android システムは、android.view.animation package 内に一般的なインターポレータのセットを提供します。上記のいずれもニーズに合わない場合は、TimeInterpolator インターフェースを実装して独自のインターフェースを作成できます。

例として、デフォルトの interpolator AccelerateDecelerateInterpolatorLinearInterpolator で補間された割合がどのように計算されるかを比較します。LinearInterpolator は、経過した割合には影響しません。AccelerateDecelerateInterpolator はアニメーション中に加速し、減速します。次のメソッドは、これらのインターポレータのロジックを定義します。

AccelerateDecelerateInterpolator

Kotlin

override fun getInterpolation(input: Float): Float =
        (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f

Java

@Override
public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

Kotlin

override fun getInterpolation(input: Float): Float = input

Java

@Override
public float getInterpolation(float input) {
    return input;
}

次の表は、1,000 ミリ秒続くアニメーションについて、これらの interpolator によって計算されるおおよその値を示しています。

経過時間(ms) 経過した割合 / 補間された割合(線形) 補間された割合(加速 / 減速)
0 0 0
200 .2 .1
400 .4 .345
600 .6 .8
800 .8 .9
1,000 1 1

表に示すように、LinearInterpolator は同じ速度で値を変更します(200 ミリ秒ごとに 0.2)。AccelerateDecelerateInterpolator は、200 ~ 600 ms の間は LinearInterpolator よりも速く、600 ~ 1,000 ms の間はゆっくりと値を変更します。

キーフレームを指定する

Keyframe オブジェクトは、アニメーションの特定時点における特定の状態を定義できる時間と値のペアで構成されます。また、各キーフレームに独自の補間子を設定し、前のキーフレームの時間からそのキーフレームの時間までの間隔におけるアニメーションの動作を制御することもできます。

Keyframe オブジェクトをインスタンス化するには、ファクトリ メソッド ofInt()ofFloat()ofObject() のいずれかを使用して、適切なタイプの Keyframe を取得する必要があります。次に、ofKeyframe() ファクトリ メソッドを呼び出して PropertyValuesHolder オブジェクトを取得します。オブジェクトを取得したら、PropertyValuesHolder オブジェクトとアニメーション化するオブジェクトを渡すことで、アニメーターを取得できます。次のコード スニペットは、これを行う方法を示しています。

Kotlin

val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
    duration = 5000
}

Java

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000);

ビューをアニメーション化する

プロパティ アニメーション システムを使用すると、ビュー オブジェクトのアニメーションを合理化できます。また、ビュー アニメーション システムに比べていくつかの利点があります。ビュー アニメーション システムは、View オブジェクトの描画方法を変更して変換しました。ビュー自体には操作するプロパティがないため、この処理は各ビューのコンテナで行われていました。その結果、ビュー オブジェクトはアニメーション化されましたが、ビュー オブジェクト自体は変更されませんでした。そのため、オブジェクトが画面上の別の場所に描画されたにもかかわらず、元の場所に残ったままになるといった動作が発生しました。Android 3.0 では、この欠点を解消するために、新しいプロパティと、対応するゲッター メソッドとセッター メソッドが追加されました。

プロパティ アニメーション システムでは、View オブジェクトの実際のプロパティを変更することで、画面上の View をアニメーション化できます。また、ビューはプロパティが変更されるたびに invalidate() メソッドを自動的に呼び出し、画面を更新します。プロパティ アニメーションを容易にする View クラスの新しいプロパティは次のとおりです。

  • translationXtranslationY: これらのプロパティは、ビューの位置を、レイアウト コンテナで設定された左端と上端の座標からの差分として制御します。
  • rotationrotationXrotationY: これらのプロパティは、ピボット ポイントを中心とした 2D(rotation プロパティ)と 3D の回転を制御します。
  • scaleXscaleY: これらのプロパティは、ピボット ポイントを中心とする View の 2D スケーリングを制御します。
  • pivotXpivotY: これらのプロパティは、回転とスケーリングの変換が行われるピボット ポイントの場所を制御します。デフォルトでは、ピボット ポイントはオブジェクトの中心に配置されます。
  • xy: コンテナ内のビューの最終的な位置を、左と上の値、translationX と translateY の値の合計として示す、シンプルなユーティリティ プロパティです。
  • alpha: ビューのアルファ透明度を表します。この値はデフォルトで 1(不透明)で、値 0 は完全な透明(非表示)を表します。

View オブジェクトのプロパティ(色や回転値など)をアニメーション化するために必要なのは、プロパティ アニメーターを作成し、アニメーション化する View プロパティを指定することだけです。次に例を示します。

Kotlin

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)

Java

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

アニメーターの作成について詳しくは、ValueAnimatorObjectAnimator によるアニメーション化のセクションをご覧ください。

ViewPropertyAnimator を使用してアニメーション化する

ViewPropertyAnimator を使用すると、基盤となる 1 つの Animator オブジェクトを使用して、View の複数のプロパティを並行してアニメーション化できます。これはビューのプロパティの実際の値を変更するため、ObjectAnimator とよく似ていますが、多数のプロパティを一度にアニメーション化する方が効率的です。また、ViewPropertyAnimator を使用するコードの方がはるかに簡潔で、読みやすくなっています。次のコード スニペットは、ビューの x プロパティと y プロパティを同時にアニメーション化する際に、複数の ObjectAnimator オブジェクト、1 つの ObjectAnimatorViewPropertyAnimator を使用する場合の違いを示しています。

複数の ObjectAnimator オブジェクト

Kotlin

val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
    playTogether(animX, animY)
    start()
}

Java

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

1 つの ObjectAnimator

Kotlin

val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()

Java

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();

ViewPropertyAnimator

Kotlin

myView.animate().x(50f).y(100f)

Java

myView.animate().x(50f).y(100f);

ViewPropertyAnimator の詳細については、対応する Android デベロッパー ブログ投稿をご覧ください。

XML でアニメーションを宣言する

プロパティ アニメーション システムを使用すると、プロパティ アニメーションをプログラムで宣言する代わりに XML で宣言できます。XML でアニメーションを定義すると、複数のアクティビティで簡単にアニメーションを再利用でき、アニメーション シーケンスの編集も簡単になります。

新しいプロパティ アニメーション API を使用するアニメーション ファイルと、以前のビュー アニメーション フレームワーク(Android 3.1 以降)を使用しているアニメーション ファイルを区別するには、プロパティ アニメーションの XML ファイルを res/animator/ ディレクトリに保存する必要があります。

次のプロパティ アニメーション クラスでは、次の XML タグによる XML 宣言がサポートされています。

XML 宣言で使用できる属性を確認するには、アニメーション リソースをご覧ください。次の例では、2 つのオブジェクト アニメーションのセットを順番に再生し、最初のネストされたセットで 2 つのオブジェクト アニメーションを同時に再生します。

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

このアニメーションを実行するには、アニメーション セットを開始する前に、コード内の XML リソースを AnimatorSet オブジェクトにインフレートしてから、すべてのアニメーションの対象オブジェクトを設定する必要があります。setTarget() を呼び出すと、便宜上、AnimatorSet のすべての子に対して単一の対象オブジェクトが設定されます。次のコードに、その方法を示します。

Kotlin

(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
    setTarget(myObject)
    start()
}

Java

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animator.property_animator);
set.setTarget(myObject);
set.start();

次の例に示すように、XML で ValueAnimator を宣言することもできます。

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

上記の ValueAnimator をコードで使用するには、次のコードに示すように、オブジェクトをインフレートして AnimatorUpdateListener を追加し、更新されたアニメーション値を取得して、いずれかのビューのプロパティで使用する必要があります。

Kotlin

(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
    addUpdateListener { updatedAnimation ->
        textView.translationX = updatedAnimation.animatedValue as Float
    }

    start()
}

Java

ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
        R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

xmlAnimator.start();

プロパティ アニメーションを定義するための XML 構文については、アニメーション リソース をご覧ください。

UI パフォーマンスに及ぶおそれのある影響

アニメーションで UI を更新すると、アニメーションが実行されるすべてのフレームで追加のレンダリング処理が発生します。このため、リソースを大量に消費するアニメーションを使用すると、アプリのパフォーマンスに悪影響を及ぼす可能性があります。

UI をアニメーション化するために必要な作業は、レンダリング パイプラインのアニメーション ステージに追加されます。アニメーションがアプリのパフォーマンスに影響するかどうかを確認するには、[GPU レンダリングのプロファイル作成] を有効にしてアニメーション ステージをモニタリングします。詳細については、 GPU レンダリングのプロファイル作成のチュートリアルをご覧ください。