2012年8月5日日曜日

androidの標準アニメーションで円を描く

結論から言うと惜しくも単独のアニメーションリソースではダメでした…!

現場で使える[逆引き+実践] Androidプログラミングテクニック」という本で、円運動させるのに独自をViewを使っていて、「標準アニメーションは、指定の位置へ一定の時間をかけて直線的に移動することはできます」とDISられてたので、円運動くらいは標準アニメーションにもできるんじゃないの?と試してみたのです。

androidやiOS、jQueryなど手軽にアニメーションを扱えるAPI全般に言えることですが、あれらはすべてプロパティ(座標やカラーコードなど)を数学的に補完しているだけなので、望む動きをどうやって数学的に表現するかというのを考えればいいのです。

---

レギュレーションとして、API Lv1から存在する標準アニメーション(ビューアニメーション)の、最初から存在する機能のみで作ります。

複数のアニメーション要素を重ね合わせられるsetで、x軸とy軸に異なる補完関数を設定したtranslateを重ねれば、円運動くらいは簡単に作れるはずです。

しかしここで想定外の出来事が。translatetranslateを4つ以上重ねると、アニメーションが正常に機能しない…。計算が複雑になりすぎると失敗するんでしょうね…。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false" >

    <translate
        android:duration="1000"
        android:fromXDelta="-300%"
        android:interpolator=
       "@android:anim/accelerate_decelerate_interpolator"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:toXDelta="300%" />
    <translate
        android:duration="500"
        android:fromYDelta="0%"
        android:interpolator=
        "@android:anim/decelerate_interpolator"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:toYDelta="300%" />
    <translate
        android:duration="500"
        android:fromYDelta="0%"
        android:interpolator=
        "@android:anim/decelerate_interpolator"
        android:startOffset="1000"
        android:toYDelta="-300%" />

</set>

上記アニメーションを以下のコードで呼び出して、widthとheightが同じ長さの適当なViewを動かしてみてください。
//適当な正方形のviewを画面中央に配置
final View view = (View)findViewById(R.id.hogeview);

//適当なボタン        
Button button = (Button)findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
     Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.guruguru);
     textView.startAnimation(animation);
  }
});

残念ながら完全な円ではなくて、270度回転になってしまいましたが、単独の標準アニメーションリソースでも、これだけのものは作れます。リスナを使ってアニメーション同士をチェインさせれば完全な円も作れました。

一応解説すると、最初のtranslateがX軸の動きです。AccelerateDecelerateInterpolatorにより、最初と最後に減速するので、ちょうど円運動のX軸の運動になります。

二つ目のtranslateはY軸の180度分の動きです。DecelerateInterpolatorは最後に減速するので、これをreverseで2回アニメーションさせると、半円のY軸の運動になります。

三つ目のtranslateは二つ目のアニメーションの、移動方向を反転させたものです。startOffsetによって開始時間を遅延させることで、入れ替わりで機能するようにします。ここではrepeatは使えません。startOffsetの待機も繰り返してしまうためです。

これで四つ目を加えて正常に動けば、単独のアニメーションリソースで円運動できたんですけどねー。

---

X軸とY軸をバラせるので、理論的にはどんな動きでもできると思います。

特にコード上でアニメーションを生成するならば、自作の補完関数を使える(自作InterpolatorをXML上で呼び出す手段もきっとあるような気がしますが)ので、複雑奇怪な動きもいろいろできるはず。

また「タップした位置をアニメーションの基点とする」「移動先が動的に変化する」ようなアニメーションは、タップ位置を変数として持たないといけないので、コード上でのみ実現できます。

実際にコード上アニメーション生成と自作補完関数を使ってみると、円を描くのももっと簡単にできます。androidの標準アニメーションで円を描く(2)で実際試してます。

---

標準アニメーションは残念な部分も多いのですけども。

おそらくスマートフォンにとって気持ちいいUIの重要性は高いということをGoogleも考えていて、API Lv1の時点でアニメーションフレームワークを練って、端末の多様性や速度など諸々のトレードオフを熟慮しつつこういう仕様になってるんだろうなという感があります。

プロパティアニメーション一本化はまだまだ無理でしょうし、標準アニメーションを上手く使いこなすことが、リッチなアプリケーションを作るうえで重要な課題かなーと思います。

「デザイナーにも簡単に作れるjQuery」によってHTMLでもリッチな体験が可能になった今、アプリはそこを主戦場にしても厳しいのかもしれませんけどね。でもだとしたらアプリでできる差別化ってなんだろー…。

0 件のコメント:

コメントを投稿