2014年2月26日

プログラミング

AndroidでもiPhoneに負けないようなアニメーションを実装してみよう

  • このエントリーをはてなブックマークに追加

こんにちは、ヤフーニュース開発の加藤真也です。
ヤフーニュースでは主にAndroidアプリの開発をしていたのですが、現在は スター育成プログラム という社内ベンチャーの枠組みで、mato*memoというAndroidアプリの開発をしています。

ふって簡単メモ! mato*memo ~まとめも~

QRコード

今回は、mato*memoアプリ開発でアニメーションを実装するため使用した、Property Animationについて紹介したいと思います。
ちなみに開発当初の目標は、タイトルにもある通り、iPhoneにも負けないアニメーションをAndroidで実装することでした。
結果がどうなったかは、上のリンクから実際にアプリをダウンロードして確認してみてください^^;

Property Animationとは

Android 3.0(API level 11)からサポートされているアニメーションフレームワークです。
アニメーションフレームワークにはAndroid 1.0(API level 1)からサポートされているView Animationというものもあるのですが、そちらはViewに対してのみしかアニメーションを実行できませんでした。
ですが、今回紹介するProperty AnimationはViewに限らず任意のオブジェクト(View,Drawable,Fragment,Object,その他すべて)に対してアニメーションを実行できます。
また、View Animationは視覚的な変化だけ(Viewの実態は変化しない)だったので、アニメーション終了時にその状態を維持するためには、setFilter( true )を使用する必要がありましたが、Property Animationは実態に対してアニメーションを実行できますので、その必要はありません。
ちなみに、Property AnimationのPropertyとは、アニメーションに必要な設定値(位置や透明度など)を指しています。

Property Animationを構成するクラス

Property Animationを構成するクラスは以下のようになっています。
Property Animationを構成するクラス

Animator

Property Animationのための基本的なメソッドが定義されているスーパークラスです。
抽象クラスとして定義されています。

ValueAnimator

Animatorを継承したクラスです。
アニメーションを実行するために、アニメーション中の値を計算して、それらの値をターゲットオブジェクトに設定するための、簡単なタイミングエンジンを提供します。
ValueAnimator自体はオブジェクトとプロパティーに値を設定できないので、アニメーションさせたいオブジェクトに、自身のロジックで値を反映させていく必要があります。
その手間を省いてくれるのが次に紹介するObjectAnimatorです。

ObjectAnimator

ValueAnimatorを継承したクラスです。
オブジェクトのプロパティーに値を設定する処理が追加されています。

指定できるプロパティーには以下のようなものがあります。

translationX
ViewのX方向の位置です。親のViewGroupの左からの座標となります。

translationY
ViewのY方向の位置です。親のViewGroupの上からの座標となります。

rotaion
ViewのZ軸周りの回転角度です。

rotaionX
ViewのX軸周りの回転角度です。

rotaionY
ViewのY軸周りの回転角度です。

scaleX
ViewのX方向の拡大率です。

scaleY
ViewのY方向の拡大率です。

alpha
Viewの透過度です。

AnimatorSet

Animatorを継承したクラスです。
複数のAnimatorをグループ化するときに使用します。

それでは、実際にProperty Animationを使用してアニメーションを実装していってみましょう。
目標はmato*memoアプリのメニューを開くときのアニメーションです。

mato*memoメニュースクリーンショット

実際の動きは、アプリをダウンロードして確認してみてください!

ふって簡単メモ! mato*memo ~まとめも~

QRコード

透過

それでは、まずはmato*memoアプリのメニューアイコンを3秒かけて表示してみます。
以下がサンプルコードです。

/**
 * 3秒かけてターゲットを表示
 * 
 * @param target
 */
private void animateAlpha( ImageView target ) {

    // alphaプロパティを0fから1fに変化させます
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat( target, "alpha", 0f, 1f );

    // 3秒かけて実行させます
    objectAnimator.setDuration( 3000 );

    // アニメーションを開始します
    objectAnimator.start();
}

上記メソッドを実行すると以下のようなアニメーションが実行されます。

mato*memoメニュー透過アニメーション

※Property AnimationはXMLでも定義できますが、Android4.0で動作しないバグがあるので、今回はコードで指定します。

移動

次はアイコンをX方向に3秒かけて200移動させてみます。
以下がサンプルコードです。

/**
 * X方向にターゲットを3秒かけて200移動する
 * 
 * @param target
 */
private void animateTranslationX( ImageView target ) {

    // translationXプロパティを0fから200fに変化させます
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat( target, "translationX", 0f, 200f );

    // 3秒かけて実行させます
    objectAnimator.setDuration( 3000 );

    // アニメーションを開始します 
    objectAnimator.start();
}

上記メソッドを実行すると以下のようなアニメーションが実行されます。

mato*memoメニュー移動アニメーション

回転

次は3秒かけてアイコンをZ軸周りに360度回転させてみます。
以下がサンプルコードです。

/**
 * 3秒かけてターゲットをZ軸周りに360度回転させる
 * 
 * @param target
 */
private void animateRotation( ImageView target ) {

    // rotationプロパティを0fから360fに変化させます
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat( target, "rotation", 0f, 360f );

    // 3秒かけて実行させます
    objectAnimator.setDuration( 3000 );

    // アニメーションを開始します
    objectAnimator.start();
}

上記メソッドを実行すると以下のようなアニメーションが実行されます。

mato*memoメニュー回転アニメーション

複数のプロパティーを同時にアニメーションさせる

次は2秒かけて、引数に与えた距離と角度の位置に、アイコンを回転させながら移動させてみます。
今回の動きを実装するには、複数のプロパティーを同時にアニメーションさせる必要があります。
そこで、PropertyValuesHolderを使用します。
PropertyValuesHolderを使用すると、一つのObjectAnimatorに複数のプロパティーを同時にアニメーションできます。
以下がサンプルコードです。

/**
 * 2秒かけて引数に与えた角度と距離の位置に回転させながらターゲットを移動させる
 * 
 * @param target
 * @param degree
 * @param distance 
 */
private void animatePropertyValuesHolderSample( ImageView target, float degree, float distance ) {

    // 距離と角度から到達点となるX座標、Y座標を求めます
    float toX = (float) ( distance * Math.cos( Math.toRadians( degree ) ) );
    float toY = (float) ( distance * Math.sin( Math.toRadians( degree ) ) );

    // translationXプロパティを0fからtoXに変化させます
    PropertyValuesHolder holderX = PropertyValuesHolder.ofFloat( "translationX", 0f, toX );
    // translationYプロパティを0fからtoYに変化させます
    PropertyValuesHolder holderY = PropertyValuesHolder.ofFloat( "translationY", 0f, toY );
    // rotationプロパティを0fから360fに変化させます
    PropertyValuesHolder holderRotaion = PropertyValuesHolder.ofFloat( "rotation", 0f, 360f );

    // targetに対してholderX, holderY, holderRotationを同時に実行させます
    ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(
            target, holderX, holderY, holderRotaion );

    // 2秒かけて実行させます
    objectAnimator.setDuration( 2000 );

    // アニメーションを開始します
    objectAnimator.start();
}

上記メソッドを実行すると以下のようなアニメーションが実行されます。
引数には角度45度、距離-200を与えています。

PropertyValuesHolderSampleアニメーション

AnimatorSetで複数のアニメーションをグループ化する

最後に複数のアニメーションをグループ化してみます。
さきほどの、アニメーションの前に2秒かけてターゲットを表示するアニメーションを加えたいと思います。
複数のアニメーションをグループ化するにはAnimatorSetを使用します。

グループ化のパターンには以下のようなものがあります。

playTogether( List<Animator> list )
引数に指定したAnimatorを同時に実行します

playSequentially( List<Animator> list )
引数に指定したAnimatorを順番に実行します

with( Animator animator )
引数に指定されたAnimatorと同時に実行します

before( Animator animator )
引数に指定されたAnimatorの前に実行します

after( Animator animator )
引数に指定されたAnimatorの後に実行します

以下がサンプルコードです。

/**
 * 2秒かけてターゲットを表示した後に、2秒かけて引数に与えた角度と距離の位置に回転させながら移動させる
 * 
 * @param target
 * @param degree
 * @param distance
 */
private void animateAnimatorSetSample( ImageView target, float degree, float distance ) {

    // AnimatorSetに渡すAnimatorのリストです
    List<Animator> animatorList= new ArrayList<Animator>();

    // alphaプロパティを0fから1fに変化させます
    ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat( target, "alpha", 0f, 1f );
    // 2秒かけて実行させます
    alphaAnimator.setDuration( 2000 );
    // リストに追加します
    animatorList.add( alphaAnimator );

    // 距離と半径から到達点となるX座標、Y座標を求めます
    float toX = (float) ( distance * Math.cos( Math.toRadians( degree ) ) );
    float toY = (float) ( distance * Math.sin( Math.toRadians( degree ) ) );

    // translationXプロパティを0fからtoXに変化させます
    PropertyValuesHolder holderX = PropertyValuesHolder.ofFloat( "translationX", 0f, toX );
    // translationYプロパティを0fからtoYに変化させます
    PropertyValuesHolder holderY = PropertyValuesHolder.ofFloat( "translationY", 0f, toY );
    // rotationプロパティを0fから360に変化させます
    PropertyValuesHolder holderRotaion = PropertyValuesHolder.ofFloat( "rotation", 0f, 360f );

    // targetに対してholderX, holderY, holderRotationを同時に実行します
    ObjectAnimator translationXYAnimator =
            ObjectAnimator.ofPropertyValuesHolder( target, holderX, holderY, holderRotaion );
    // 2秒かけて実行させます
    translationXYAnimator.setDuration( 2000 ); 
    // リストに追加します
    animatorList.add( translationXYAnimator );

    final AnimatorSet animatorSet = new AnimatorSet();
    // リストのAnimatorを順番に実行します
    animatorSet.playSequentially( animatorList );

    // アニメーションを開始します
    animatorSet.start();
} 

上記メソッドを実行すると以下のようなアニメーションが実行されます。
引数には角度45度、距離-200を与えています。

AnimatorSetSampleアニメーション

これでターゲットを、回転させながら指定した距離と角度の位置に移動させるアニメーションを実装することができました!
あとは、これを複数のターゲットに対して角度と実行タイミングを調整しながら実行すれば、mato*memoアプリのメニューのような動きが実装できるはずなので、続きの実装は、ぜひ挑戦してみてください!
※ちなみに、アニメーションの実行タイミングを変化させるには、Animator.setStartDelay(long startDelay)を使うと良いと思います。

最後に

今回はProperty Animationをmato*memoアプリでの実装例と交えて紹介しました。
Property Animationを使用すると、シンプルな記述でほとんどのアニメーションを実装できるので、ぜひ活用してみてください。
mato*memoアプリもよろしくお願い致します。

ふって簡単メモ! mato*memo ~まとめも~
QRコード

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加