Edited at

Androidのアニメーション処理に関して

More than 1 year has passed since last update.


重要な追記

今でもたまにストックされるので、追記しておくと、CODE PATH や公式リファレンスのアニメーション一覧が充実してきたのでこちらを参照した方がよいかも。


Animations

Android にどういうアニメーションがあるのかサンプルコード付きでだいたい記載されています。

ちょっとしたアニメーションを記述するときの作法は、ViewCompat.animate()を使うのが良さそうですね。

ViewCompat.animate(binding.cardView)

.setDuration(400)
.scaleXBy(1.5f)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();

注意点として、v4 サポートライブラリなのだけれど、実装はViewPropertyAnimatorで、なぜか ICE 以上でしか動作しません。ICE 以下を切り捨てる前提で使う必要があります。

また、ICE 以上でも、elevationなどバージョンによって動作しないプロパティがありますが、ドキュメントに記載があるので特に困ることはないと思います。


カスタム アニメーションの定義

日本語化されたドキュメントもあります。

こちらは、CODE PATH には書いていないAnimatedVectorDrawable(ベクター画像のアニメーション)についても触れています。

あとは、マテリアルデザイン関連で追加されたInterpolatorの解説が欲しいです…。


以下、旧文書(一部修正)

Androidにはアニメーションの手段が多数あります。例えば、


  • Android1.0から存在するTransition Drawable

  • Android1.0から存在するView Animation

  • Android3.0で追加されたProperty Animation

  • Android4.4で追加されたTransition Animation

  • Android5.0で追加されたShared Element Activity Transition

などがあります。


Transition Drawable

Layer Drawableの変種で、複数のDrawableをフェードで切り替えられるDrawableです。


tran_drawable.xml

<?xml version="1.0" encoding="utf-8"?>

<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/normal"/>
<item android:drawable="@drawable/transform"/>
</transition>

TransitionDrawable td = (TransitionDrawable) hogeView.getBackground();

// Drawableを切り替える
td.startTransition(300);
// td.reverseTransition();で元に戻せる

これ地味に便利です。


View Animation

全てのAndroid端末で利用できます。貧弱な携帯端末で、機敏なアニメーションを実現ためにViewの表示部分のみをアニメーションさせるレガシーなアニメーションAPIです。

AppCompatActivityを採用する場合、トランジションはこちらで書く必要があるため、現在でもたまに利用されますね。


  • ビューに対してアフィン変換を実行する

  • フレームアニメーションというものもあり、アニメーションGIFのような挙動もする


  • AnimationSetで複合アニメーションも可能


hoge.xml

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<translate
android:fromXDelta="0%p"
android:toXDelta="100%p"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
</set>

// アニメーションXMLを/anim/に定義

Animation animation = AnimationUtils.loadAnimation(this, R.anim.hoge);
view.startAnimation(animation);

本当に「見た目だけ」のアニメーションで、タップ判定などはそのまま残ります。


Property Animation

Android3.X系から追加された、Viewの任意のプロパティ値に対して操作を行うアニメーションです。


  • 各プロパティを線形的に変化させるObjectAnimatorをメインで使う

  • 見た目だけ変化していたView Animationとは異なり、実際の位置も変更される

  • 複数のプロパティを操作するときはPropertyValuesHolderを使う

  • 複数のViewを扱うときはAnimatorSetを用いる

  • 自分で作ったカスタムビューのプロパティ操作も可能。メーター系ビューと組み合わせよう


property_animation.xml

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"

android:propertyName="alpha"
android:duration="500"
android:valueTo="1f"/>

// /anim/から/animator/に変わりましたが、別に変えなくても動くらしい?

Animator anim = AnimatorInflater.loadAnimator(this, R.animator.property_animator);
set.setTarget(hogeView);
set.start();

〜2.Xでしか使えない旧来のアニメーションと区別するため、「Animation」から「Animator」に名称変更したのですが、経緯を知らないと分かりづらいです。

また、どのプロパティが操作可能なのかいまいちドキュメント化されていない印象があります。

ただし非公式互換ライブラリがありますので、2.Xでも動かそうと思えば動きます。なぜ公式でバックポートする気がないのかは分かりません。


Transition Animation

KitKatから追加されたマイナーなアニメーションですが、のちのマテリアルモーションの土台となっています。

SceneオブジェクトにViewGroupを渡すと、フェードやサイズ変更の遷移のアニメーションを実行してくれます。

従来のアニメーションでは単一のViewを対象としていましたが、トランジションではViewGroupを対象にします。同時に各Viewのアニメーション可能プロパティなどの詳細を知らなくても、ある程度自動で補完してくれます。

この時点では、画面内の複数の状態を、Sceneという概念で扱い、それらをトランジションで切り替えるという発想だったように思います。


tran_set.xml

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"

android:ordering="sequential">
<fade/>
<changeBounds/>
</transitionSet>

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

ViewGroup container = (ViewGroup)findViewById(R.id.container);
View hogeView = LayoutInflater.from(this).inflater(R.layout.hoge, null);
Scene scene = new Scene(container, hogeView);
TransitionInflater inflater = TransitionInflater.from(this);
Transition transition = inflater.inflateTransition(R.transition.tran_set.xml);
TransitionManager.go(scene, transition);

Sceneを用いたトランジションは、アニメーションだけでなく、ViewGroup内に既に存在しているViewを新しいViewで置き換えます。


Shared Element Activity Transition

複数画面でViewを共有するトランジションを作成できます。

getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);

getWindow().setSharedElementExitTransition("share");

Pair<View, String> share = Pair.create(mShareView, "share");
Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(this, share).toBundle();
startActivity(new Intent(this, FugaActivity.class), bundle);

ざっくり言うと、「Viewに名前をつけて次のActivityに渡すと、そのActivity内で同じ名前を持っているViewに対してTransition Animationが発生する」という感じです。

同じことはFragmentでも実現できます。

これにより、トランジションがどういうコンテキストで現在の画面を表示しているのかを明確にします。濫用しすぎると意味が分からないアニメーションになるので、ユーザーの視線を誘導することを意識すると良いそうです。