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

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

プロパティ アニメーション システムでは、アニメーションの次のプロパティを定義できます。

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

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

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

まず、簡単な例を使用してアニメーションの仕組みについて説明します。図 1 は、画面上の水平方向の位置を表す x プロパティを使用して、アニメーション化される仮想オブジェクトを示しています。アニメーションの継続時間は 40 ms、移動距離は 40 ピクセルに設定されています。デフォルトのフレーム更新頻度である 10 ms ごとに、オブジェクトが水平方向に 10 ピクセルずつ移動します。40 ms が経過すると、アニメーションは停止し、オブジェクトは水平位置 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 ms での経過した割合(0.25)よりも小さくなります。図 1 では、補間された割合は常に経過した割合と同じです。

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

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

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

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

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

しかし、ビュー アニメーション システムのほうが設定に時間がかからず、作成に必要なコードも少なくて済みます。ビュー アニメーションで必要な処理がすべて完了している場合、または既存のコードがすでに意図したとおり動作している場合は、プロパティ アニメーション システムを使用する必要はありません。また、ユースケースが発生した場合に、状況に応じて両方のアニメーション システムを使用することも理にかなっています。

API の概要

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

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

表 1. アニメーター

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

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

表 2. エバリュエータ

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

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

表 3. インターポレータ

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

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

ValueAnimator クラスを使用すると、アニメーション化に使用する intfloat、または color の値のセットを指定することで、アニメーションの継続時間中に一部のタイプの値をアニメーション化できます。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() メソッドが実行されたときに、MyTypeEvaluator によるロジックを使用して ValueAnimator がアニメーションの値(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(float) が必要です。
        ObjectAnimator.ofFloat(targetObject, "propName", 1f)
        
  • アニメーション化するプロパティまたはオブジェクトによっては、ビューで invalidate() メソッドを呼び出して、更新されたアニメーション値で画面を強制的に再描画する必要があります。これは onAnimationUpdate() コールバックで行います。たとえばドローアブル オブジェクトの色のプロパティをアニメーション化すると、そのオブジェクトが再描画されたときにのみ、画面が更新されます。setAlpha()setTranslationX() など、ビューのプロパティ セッターはすべてビューを適切に無効化するため、新しい値でこれらのメソッドを呼び出すとき、ビューを無効にする必要はありません。リスナーの詳細については、アニメーション リスナーのセクションをご覧ください。

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() など、ビューのプロパティ セッターはすべてビューを適切に無効化するため、新しい値でこれらのメソッドを呼び出すとき、ビューを無効にする必要はありません。

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 オブジェクトへの変更をアニメーション化するだけでなく、ビュー オブジェクト自体も簡単にアニメーション化できます。

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

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

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

API デモの LayoutAnimations サンプルで、レイアウト遷移のアニメーションを定義し、アニメーション化するビュー オブジェクトのアニメーションを設定する方法を示しています。

LayoutAnimationsByDefault と、その対応するlayout_animations_by_default.xml レイアウト リソース ファイルでは、XML で ViewGroups のデフォルトのレイアウト遷移を有効にする方法を示しています。行う必要があるのは、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 から削除されたビューと、ViewGroup の残りのビューが自動的にアニメーション化されます。

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

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

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、または color であり、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)を実行すると、アニメーションの現在の経過した割合が計算され、使用しているインターポレータに応じて補間バージョンが計算されます。補間された割合は、fraction パラメータを通じて TypeEvaluator が受け取るものであるため、アニメーション化された値を計算するときにインターポレータを考慮する必要はありません。

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

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

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

例として、デフォルトのインターポレータである 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 ms のアニメーションについてこれらのインターポレータで計算される近似値を示しています。

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

表に示すように、LinearInterpolator は同じ速さで値を変更します(200ms ごとに 0.2 ずつ)。AccelerateDecelerateInterpolator は、200ms から 600ms では LinearInterpolator よりも速く値を変更し、600ms から 1,000ms では遅く変更します。

キーフレームを指定する

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

Keyframe オブジェクトをインスタンス化するには、ファクトリ メソッド(ofInt()ofFloat()ofObject())のいずれかを使用して、該当するタイプの を取得する必要があります。次に 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 ではこの欠点を解消するために、新しいプロパティと、対応するゲッター メソッドとセッター メソッドが追加されました。

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

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

色や回転の値など、ビュー オブジェクトのプロパティをアニメーション化するには、プロパティ アニメーターを作成し、アニメーション化するビュー プロパティを指定するだけで済みます。次に例を示します。

Kotlin

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

Java

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

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

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

ViewPropertyAnimator は、基になる単一の Animator オブジェクトを使用して、View の複数のプロパティを並行してアニメーション化する簡単な方法を提供します。ビューの実際のプロパティ値を変更するため、これは ObjectAnimator のように動作しますが、一度に多くのプロパティをアニメーション化するほうが効率的です。さらに、ViewPropertyAnimator を使用するコードのほうが、はるかに簡潔で読みやすくなります。次のコード スニペットは、ビューの x プロパティと y プロパティを同時にアニメーション化する場合の、複数の ObjectAnimator オブジェクト、単一の 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 レンダリングのプロファイル作成のチュートリアルをご覧ください。