物理ベースの運動は力によって駆動されます。バネの力は、インタラクティビティと動きを導く力のひとつです。ばねの力には、減衰と剛性という特性があります。ばねベースのアニメーションでは、値と速度は各フレームに適用されるばねの力に基づいて計算されます。
アプリのアニメーションを一方向にのみ遅くしたい場合は、代わりに摩擦ベースのフリング アニメーションの使用を検討してください。
スプリング アニメーションのライフサイクル
ばねベースのアニメーションでは、SpringForce
クラスを使用して、ばねの剛性、減衰率、最終位置をカスタマイズできます。アニメーションが開始されるとすぐに、ばねの力によって各フレームのアニメーション値と速度が更新されます。アニメーションは、ばねの力が平衡に達するまで続きます。
たとえば、画面上でアプリアイコンをドラッグし、その後アイコンから指を離して離すと、目に見えないがなじみのある力によってアイコンが元の位置に戻ります。
図 1 は、同様のばねの効果を示しています。円の中央にあるプラス記号(+)は、タップ操作によって適用される力を示します。
スプリング アニメーションをビルドする
アプリのスプリング アニメーションを作成する一般的な手順は次のとおりです。
- サポート ライブラリを追加するスプリング アニメーション クラスを使用するには、プロジェクトにサポート ライブラリを追加する必要があります。
- スプリング アニメーションを作成する: 主なステップは、
SpringAnimation
クラスのインスタンスを作成し、モーション動作パラメータを設定することです。 - (省略可)リスナーを登録する: リスナーを登録して、アニメーションのライフサイクルの変更とアニメーション値の更新を監視します。
注: 更新リスナーは、アニメーション値の変更についてフレームごとに更新する必要がある場合にのみ登録する必要があります。更新リスナーは、アニメーションが別のスレッドで実行されるのを防ぎます。
- (省略可)リスナーを削除する: 使用されなくなったリスナーを削除します。
- (省略可)開始値を設定する: アニメーションの開始値をカスタマイズします。
- (省略可)値の範囲を設定する: アニメーション値の範囲を設定して、最小値と最大値の範囲内の値を制限します。
- (省略可)開始速度を設定する: アニメーションの開始速度を設定します。
- (省略可)ばねの特性を設定する: ばねの減衰率と剛性を設定します。
- (省略可)カスタム スプリングを作成する: デフォルトのスプリングを使用しない場合、またはアニメーション全体で共通のスプリングを使用する場合は、カスタム スプリングを作成します。
- アニメーションを開始: スプリング アニメーションを開始します。
- (省略可)アニメーションをキャンセルする: ユーザーがアプリを突然終了した場合や、ビューが非表示になった場合に、アニメーションをキャンセルします。
以降のセクションでは、スプリング アニメーションを作成する一般的な手順について詳しく説明します。
サポート ライブラリを追加する
物理ベースのサポート ライブラリを使用するには、次のようにサポート ライブラリをプロジェクトに追加する必要があります。
- アプリ モジュールの
build.gradle
ファイルを開きます。 サポート ライブラリを
dependencies
セクションに追加します。Groovy
dependencies { def dynamicanimation_version = '1.0.0' implementation "androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version" }
Kotlin
dependencies { val dynamicanimation_version = "1.0.0" implementation("androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version") }
このライブラリの現在のバージョンを確認するには、バージョン ページで DynamicAnimation に関する情報をご覧ください。
スプリング アニメーションを作成する
SpringAnimation
クラスを使用すると、オブジェクトのスプリング アニメーションを作成できます。スプリング アニメーションを作成するには、SpringAnimation
クラスのインスタンスを作成し、オブジェクト、アニメーション化するオブジェクトのプロパティ、アニメーションを停止する最終的なスプリング位置(省略可)を指定する必要があります。
注: スプリング アニメーションの作成時、ばねの最終位置は任意です。ただし、アニメーションを開始する前に定義する必要があります。
Kotlin
val springAnim = findViewById<View>(R.id.imageView).let { img -> // Setting up a spring animation to animate the view’s translationY property with the final // spring position at 0. SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0f) }
Java
final View img = findViewById(R.id.imageView); // Setting up a spring animation to animate the view’s translationY property with the final // spring position at 0. final SpringAnimation springAnim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0);
スプリングベースのアニメーションでは、ビュー オブジェクトの実際のプロパティを変更することで、画面上のビューをアニメーション化できます。システムで使用できるビューは次のとおりです。
ALPHA
: ビューのアルファ透明度を表します。デフォルトの値は 1(不透明)で、0 は完全な透明度(非表示)を表します。TRANSLATION_X
、TRANSLATION_Y
、TRANSLATION_Z
: これらのプロパティは、ビューの位置を、レイアウト コンテナで設定された左座標、上座標、高度からの差分として制御します。TRANSLATION_X
は左座標を表します。TRANSLATION_Y
は上座標を表します。TRANSLATION_Z
は、高度を基準としたビューの深度を表します。
ROTATION
、ROTATION_X
、ROTATION_Y
: これらのプロパティは、ピボット ポイントを中心とした 2D(rotation
プロパティ)と 3D の回転を制御します。SCROLL_X
、SCROLL_Y
: ソースの左端と上端のスクロール オフセットをピクセル単位で指定します。また、ページのスクロール位置も指定します。SCALE_X
、SCALE_Y
: これらのプロパティは、ピボット ポイントを中心とするビューの 2D スケーリングを制御します。X
、Y
、Z
: コンテナ内のビューの最終的な場所を記述する基本的なユーティリティ プロパティです。X
は、左側の値とTRANSLATION_X
の合計です。Y
はトップ値とTRANSLATION_Y
の合計です。Z
は、高度の値とTRANSLATION_Z
の合計です。
リスナーを登録する
DynamicAnimation
クラスには、OnAnimationUpdateListener
と OnAnimationEndListener
の 2 つのリスナーが用意されています。これらのリスナーは、アニメーション値が変更されたときや、アニメーションが終了したときなど、アニメーションの更新をリッスンします。
OnAnimationUpdateListener
複数のビューをアニメーション化してチェーン アニメーションを作成する場合は、現在のビューのプロパティが変更されるたびにコールバックを受け取るように OnAnimationUpdateListener
を設定できます。このコールバックは、現在のビューのプロパティで発生した変更に基づいてばねの位置を更新するよう、もう一方のビューに通知します。リスナーを登録するには、次の手順を行います。
-
addUpdateListener()
メソッドを呼び出して、リスナーをアニメーションにアタッチします。注: アップデート リスナーは、アニメーションを開始する前に登録する必要があります。ただし、更新リスナーは、アニメーション値の変更についてフレームごとに更新する必要がある場合にのみ登録する必要があります。更新リスナーは、アニメーションが別のスレッドで実行される可能性を防止します。
-
onAnimationUpdate()
メソッドをオーバーライドして、現在のオブジェクトの変更を呼び出し元に通知します。次のサンプルコードは、OnAnimationUpdateListener
の全体的な使用例を示しています。
Kotlin
// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties val (anim1X, anim1Y) = findViewById<View>(R.id.view1).let { view1 -> SpringAnimation(view1, DynamicAnimation.TRANSLATION_X) to SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y) } val (anim2X, anim2Y) = findViewById<View>(R.id.view2).let { view2 -> SpringAnimation(view2, DynamicAnimation.TRANSLATION_X) to SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y) } // Registering the update listener anim1X.addUpdateListener { _, value, _ -> // Overriding the method to notify view2 about the change in the view1’s property. anim2X.animateToFinalPosition(value) } anim1Y.addUpdateListener { _, value, _ -> anim2Y.animateToFinalPosition(value) }
Java
// Creating two views to demonstrate the registration of the update listener. final View view1 = findViewById(R.id.view1); final View view2 = findViewById(R.id.view2); // Setting up a spring animation to animate the view1 and view2 translationX and translationY properties final SpringAnimation anim1X = new SpringAnimation(view1, DynamicAnimation.TRANSLATION_X); final SpringAnimation anim1Y = new SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y); final SpringAnimation anim2X = new SpringAnimation(view2, DynamicAnimation.TRANSLATION_X); final SpringAnimation anim2Y = new SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y); // Registering the update listener anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() { // Overriding the method to notify view2 about the change in the view1’s property. @Override public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value, float velocity) { anim2X.animateToFinalPosition(value); } }); anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() { @Override public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value, float velocity) { anim2Y.animateToFinalPosition(value); } });
OnAnimationEndListener
OnAnimationEndListener
はアニメーションの終了を通知します。アニメーションが平衡状態に達するか、アニメーションがキャンセルされたときにコールバックを受け取るようにリスナーを設定できます。リスナーを登録するには、次の手順を行います。
-
addEndListener()
メソッドを呼び出して、リスナーをアニメーションにアタッチします。 -
アニメーションが平衡状態に達するかキャンセルされるたびに通知を受け取るように、
onAnimationEnd()
メソッドをオーバーライドします。
リスナーを削除する
アニメーション更新コールバックとアニメーション終了コールバックの受信を停止するには、それぞれ removeUpdateListener()
メソッドと removeEndListener()
メソッドを呼び出します。
アニメーション開始値を設定する
アニメーションの開始値を設定するには、setStartValue()
メソッドを呼び出して、アニメーションの開始値を渡します。開始値を設定しない場合、アニメーションはオブジェクトのプロパティの現在の値を開始値として使用します。
アニメーションの値の範囲を設定する
プロパティ値を特定の範囲に制限する場合は、アニメーションの最小値と最大値を設定できます。また、アルファ(0 ~ 1)などの固有の範囲を持つプロパティをアニメーション化する場合、範囲を制御するのにも役立ちます。
-
最小値を設定するには、
setMinValue()
メソッドを呼び出して、プロパティの最小値を渡します。 -
最大値を設定するには、
setMaxValue()
メソッドを呼び出して、プロパティの最大値を渡します。
どちらのメソッドも、値が設定されているアニメーションを返します。
注: 開始値を設定し、アニメーション値の範囲を定義している場合は、開始値が最小値と最大値の範囲内であることを確認します。
開始速度を設定する
開始速度は、アニメーションの開始時にアニメーション プロパティが変化する速度を定義します。デフォルトの開始速度は 0 ピクセル/秒に設定されています。この速度は、タッチ操作の速度で設定することも、開始速度として固定値を使用して設定することもできます。固定値を指定する場合は、dp/秒で値を定義してから、ピクセル/秒に変換することをおすすめします。dp/秒で値を定義すると、速度を密度やフォーム ファクタに依存させることができます。値を 1 秒あたりのピクセル数に変換する方法については、1 秒あたりの dp を 1 秒あたりのピクセル数に変換するをご覧ください。
速度を設定するには、setStartVelocity()
メソッドを呼び出し、ピクセル/秒単位で速度を渡します。このメソッドは、速度が設定されたばねの力のオブジェクトを返します。
注: タッチ操作の速度の取得と計算には、GestureDetector.OnGestureListener
クラスまたは VelocityTracker
クラスのメソッドを使用します。
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … // Compute velocity in the unit pixel/second vt.computeCurrentVelocity(1000) val velocity = vt.yVelocity setStartVelocity(velocity) } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … // Compute velocity in the unit pixel/second vt.computeCurrentVelocity(1000); float velocity = vt.getYVelocity(); anim.setStartVelocity(velocity);
dp/秒をピクセル/秒に変換する
ばねの速度は、ピクセル/秒である必要があります。速度の始点として固定値を指定する場合は、dp/秒で値を指定してから、ピクセル/秒に変換します。変換には、TypedValue
クラスの applyDimension()
メソッドを使用します。次のサンプルコードを参照してください。
Kotlin
val pixelPerSecond: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, resources.displayMetrics)
Java
float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, getResources().getDisplayMetrics());
ばねの特性を設定する
SpringForce
クラスは、ばねの特性(減衰率や剛性など)ごとにゲッター メソッドとセッター メソッドを定義します。ばねのプロパティを設定するには、ばねの力のオブジェクトを取得するか、プロパティを設定できるカスタムのばねの力を作成することが重要です。カスタムのばねの力を作成する方法については、カスタムのばねの力を作成するをご覧ください。
ヒント: セッター メソッドの使用中に、すべてのセッター メソッドがばねの力のオブジェクトを返すため、メソッド チェーンを作成できます。
減衰率
減衰率は、ばねの振動の漸進的な減少を表します。減衰率を使用することで、あるバウンスから次のバウンスまでの振動が減衰する速さを定義できます。ばねを減衰させる方法は 4 つあります。
- 過減衰は、減衰率が 1 より大きい場合に発生します。これにより、オブジェクトをゆっくりと静止位置に戻すことができます。
- 臨界減衰は、減衰率が 1 に等しい場合に発生します。オブジェクトを最短の位置に戻すことができます。
- 不足減衰は、減衰比が 1 未満の場合に発生します。オブジェクトを静止位置を通過させることで複数回オーバーシュートし、その後徐々に静止位置に到達します。
- 非減衰は、減衰率が 0 に等しい場合に発生します。オブジェクトを永続的に振動させることができます。
ばねに減衰率を追加する手順は、次のとおりです。
-
getSpring()
メソッドを呼び出して、ばねを取得し、減衰率を追加します。 -
setDampingRatio()
メソッドを呼び出し、ばねに追加する減衰率を渡します。このメソッドは、減衰率が設定されているばねの力のオブジェクトを返します。注: 減衰率は負でない数値でなければなりません。減衰率をゼロに設定すると、ばねが静止位置に達することはありません。つまり、永久に振動しているということです。
システムで使用できる減衰率定数は次のとおりです。
DAMPING_RATIO_HIGH_BOUNCY
DAMPING_RATIO_MEDIUM_BOUNCY
DAMPING_RATIO_LOW_BOUNCY
DAMPING_RATIO_NO_BOUNCY
図 2: 高いバウンス
図 3: 中程度の弾み
図 4: 低バウンス
図 5: 弾力なし
デフォルトの減衰率は DAMPING_RATIO_MEDIUM_BOUNCY
に設定されています。
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … // Setting the damping ratio to create a low bouncing effect. spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY … } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … // Setting the damping ratio to create a low bouncing effect. anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY); …
剛性
剛性は、ばねの強さを測定するばね定数を定義します。スプリングが硬い場合は、スプリングが静止位置にないときに、取り付けられている物体により大きな力が加えられます。ばねに剛性を追加する手順は次のとおりです。
-
getSpring()
メソッドを呼び出して、ばねを取得し、剛性を追加します。 -
setStiffness()
メソッドを呼び出し、ばねに追加する剛性の値を渡します。このメソッドは、剛性が設定されているばねの力のオブジェクトを返します。注: 剛性は正の数にする必要があります。
システムで使用できる剛性定数は次のとおりです。
図 6: 高剛性
図 7: 中程度の剛性
図 8: 低剛性
図 9: 非常に低い剛性
デフォルトの剛性は STIFFNESS_MEDIUM
に設定されています。
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … // Setting the spring with a low stiffness. spring.stiffness = SpringForce.STIFFNESS_LOW … } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … // Setting the spring with a low stiffness. anim.getSpring().setStiffness(SpringForce.STIFFNESS_LOW); …
カスタムのばねの力を作成する
デフォルトのばねの力の代わりに、カスタムのばねの力を作成することもできます。カスタムのばねの力を使用すると、複数のばねのアニメーションで同じばねの力のインスタンスを共有できます。ばねの力を作成したら、減衰率や剛性などのプロパティを設定できます。
-
SpringForce
オブジェクトを作成します。SpringForce force = new SpringForce();
-
各メソッドを呼び出してプロパティを割り当てます。メソッド チェーンを作成することもできます。
force.setDampingRatio(DAMPING_RATIO_LOW_BOUNCY).setStiffness(STIFFNESS_LOW);
-
setSpring()
メソッドを呼び出して、ばねをアニメーションに設定します。setSpring(force);
アニメーションを開始する
スプリング アニメーションを開始するには、start()
を呼び出すか、animateToFinalPosition()
メソッドを呼び出します。どちらのメソッドもメインスレッドで呼び出す必要があります。
animateToFinalPosition()
メソッドは、次の 2 つのタスクを実行します。
- ばねの最終位置を設定します。
- アニメーションが開始されていない場合、開始します。
このメソッドはばねの最終位置を更新し、必要に応じてアニメーションを開始するため、いつでもこのメソッドを呼び出してアニメーションのコースを変更できます。たとえば、チェーン スプリング アニメーションでは、あるビューのアニメーションが別のビューに依存します。このようなアニメーションには、animateToFinalPosition()
メソッドを使用する方が便利です。チェーン スプリング アニメーションでこのメソッドを使用すると、次に更新するアニメーションが実行中かどうかを心配する必要がなくなります。
図 10 は、あるビューのアニメーションが別のビューに依存するチェーン スプリング アニメーションを示しています。
animateToFinalPosition()
メソッドを使用するには、animateToFinalPosition()
メソッドを呼び出し、ばねの残りの位置を渡します。また、setFinalPosition()
メソッドを呼び出して、ばねの静止位置を設定することもできます。
start()
メソッドは、プロパティ値をすぐに開始値に設定するわけではありません。プロパティ値は、描画パスの前にアニメーション パルスが発生するたびに変化します。その結果、値がすぐに設定された場合と同様に、次のフレームで変更が反映されます。
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … // Starting the animation start() … } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … // Starting the animation anim.start(); …
アニメーションをキャンセルする
アニメーションをキャンセルまたは最後までスキップできます。アニメーションをキャンセルするか最後までスキップする必要がある理想的な状況としては、ユーザー操作によってアニメーションをすぐに終了する必要がある場合が挙げられます。これは主に、ユーザーがアプリを突然終了した場合や、ビューが非表示になった場合に発生します。
アニメーションを終了するには、2 つの方法があります。cancel()
メソッドは、現在の値でアニメーションを終了します。skipToEnd()
メソッドはアニメーションを最後の値までスキップし、その後終了します。
アニメーションを終了する前に、まずばねの状態を確認することが重要です。状態が減衰していない場合、アニメーションは静止位置に到達できません。ばねの状態を確認するには、canSkipToEnd()
メソッドを呼び出します。ばねが減衰されている場合、このメソッドは true
を返し、それ以外の場合は false
を返します。
ばねの状態を把握したら、skipToEnd()
メソッドまたは cancel()
メソッドを使用してアニメーションを終了できます。cancel()
メソッドは、メインスレッドでのみ呼び出す必要があります。
注: 一般に、skipToEnd()
メソッドを使うと表示が切り替わります。