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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ビュー アニメーション システムのもう 1 つのデメリットは、実際の 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 Sets を使用した複数のアニメーションのコレオグラフィーをご覧ください。

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

表 2. エバリュエータ

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

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

表 3: interpolator

クラス / インターフェース 説明
AccelerateDecelerateInterpolator 変化の速度の開始と終了は遅いものの、途中で加速するインターポレータ。
AccelerateInterpolator 変化の速度は開始時がゆっくりで、その後加速する interpolator。
AnticipateInterpolator 変化が逆方向に開始してから、順方向に切り替わるインターポレータ。
AnticipateOvershootInterpolator 変化が逆方向に開始し、前方にフリングしてターゲット値をオーバーシュートし、最終的に最終値に戻るインターポレータ。
BounceInterpolator 変化が最後にバウンドするインターポレータ。
CycleInterpolator 指定したサイクル数だけアニメーションが繰り返されるインターポレータ。
DecelerateInterpolator 変化の速度が最初に速くなり、その後減速するインターポレータ。
LinearInterpolator 変化の速度が一定であるインターポレータ。
OvershootInterpolator 変化がフリング フォワードし、最後の値をオーバーシュートして戻るインターポレータ。
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 がアニメーションの値(0 ~ 100、1,000 ms)の計算を開始します。

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

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 から提供されるロジックを使用して、アニメーションの値(startPropertyValueendPropertyValue)の計算を 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)
    
  • アニメーション化するプロパティまたはオブジェクトによっては、ビューで invalidate() メソッドを呼び出して、更新されたアニメーション値で画面を自動的に再描画する必要があります。これは onAnimationUpdate() コールバックで実行します。たとえば、Drawable オブジェクトの color プロパティをアニメーション化すると、そのオブジェクトが再描画されたときにのみ画面が更新されます。setAlpha()setTranslationX() など、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() を呼び出して、画面のその領域を強制的に新しいアニメーション値で再描画する必要があります。たとえば、ドローアブル オブジェクトのカラー プロパティをアニメーション化すると、そのオブジェクトが再描画されたときにのみ画面が更新されます。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 に対してビューを追加または削除するとき、または VISIBLEINVISIBLE、または GONE でビューの setVisibility() メソッドを呼び出すときに、表示 / 非表示のアニメーションが発生することがあります。ViewGroup の残りのビューも、ビューを追加または削除するときに新しい位置にアニメーション化できます。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 オブジェクトのラッパーとして動作し、指定されたビュー状態(「押下」や「フォーカス」など)が変わるたびにアニメーションを呼び出します。

StateListAnimator は、StateListAnimator クラスで定義された異なるビュー状態を指定するルート <selector> 要素と子 <item> 要素を使用して、XML リソースで定義できます。各 <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 の値)を計算し、使用している interpolator に応じて補間バージョンを計算します。補間された割合は、TypeEvaluatorfraction パラメータを介して受け取るものであるため、アニメーション化された値を計算するときに interpolator を考慮する必要はありません。

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

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

アニメーション システムの interpolator は、Animators から、アニメーションの経過時間を表す割合を受け取ります。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
1000 1 1

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

キーフレームを指定する

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

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

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

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

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

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

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

Kotlin

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

Java

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

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

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

ViewPropertyAnimator を使用すると、基盤となる単一の 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 で定義すると、複数のアクティビティでアニメーションを簡単に再利用でき、アニメーション シーケンスの編集も簡単になります。

Android 3.1 以降、新しいプロパティ アニメーション API を使用するアニメーション ファイルと以前のビュー アニメーション フレームワークを使用するアニメーション ファイルを区別するには、プロパティ アニメーションの 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 レンダリングのプロファイル作成のチュートリアルをご覧ください。