DataBindingチュートリアル第6回 – アニメーション

第5回ではユーザーの属性が変わるたびにユーザーインターフェースが切り替わる仕組みをDataBindingで実現しました。 今回はこれをさらに改良して、切り替えのタイミングにアニメーションを挿入する方法を紹介したいと思います。

OnRebindCallback

前回のコードにアニメーションをつけるのは非常にシンプルです。まずOnRebindCallbackというものをbindingオブジェクトにaddします。

binding.addOnRebindCallback(new OnRebindCallback() {
    @Override
    public boolean onPreBind(ViewDataBinding binding) {
        TransitionManager.beginDelayedTransition(
                (ViewGroup)binding.getRoot());
        return super.onPreBind(binding);
    }
});

このようにするとBindingされたデータに変更があり、Bindしたビュー側が再評価される時にコールバックを受け取ることができます。

OnRebindCallbackクラスで受け取るコールバックのうちonPreBindというメソッドをオーバライドし、この中で、TransitionManagerbeginDelayedTransitionという関数を呼びます。このメソッドは(こちらの公式ドキュメントに詳細が書かれていますが) 、beginDelayedTransitionを呼んでから次のビューの描画が行われるまでの間の全てのビューの変更をアニメーションさせるというメソッドです。
onPreBindはバインドしたビューの再評価が行われる前に呼ばれる関数なので、再評価の直前のここでこの関数を呼んでおけば直後に行われるビューの再評価と再描画の際にアニメーションが起きるようになります。

スクリーンショット

BindingAdapterを使ったアニメーション

OnRebindCallbackとTransitionManagerを使ったアニメーションはでは変更のあったデータにバインドされて見た目が変わる全てのビューにアニメーションが適用されます。
場合によっては、ある特定のビューだけをアニメーションしたい、またはTransitionManagerによって作られるアニメーションとは違ったアニメーションを割り当てたいケースがあるかもしれません。このような場合はBindingAdapterを使って個別にアニメショーンを加える方法があります。

まず、下のようにViewとvisibilityの内容を取得するBindingAdapterを作ります。  

@BindingAdapter("bind:visibilityWithAnimation")
public static void setVisibility(final View view,
                                 final int visibility) {

    //今のvisibilityから変化がないので何もしない
    if (view.getVisibility() == visibility) {
        return;
    }

    float startAlpha = view.getVisibility() == View.VISIBLE ? 1f : 0f;
    float endAlpha = visibility == View.VISIBLE ? 1f : 0f;


    ObjectAnimator animator = ObjectAnimator.ofFloat(view,
            View.ALPHA, startAlpha, endAlpha);
    animator.setAutoCancel(true);
    animator.setDuration(1000);
    animator.addListener(new AnimatorListenerAdapter() {

        @Override
        public void onAnimationStart(Animator anim) {
            view.setVisibility(View.VISIBLE);
        }

        @Override
        public void onAnimationCancel(Animator anim) {

        }

        @Override
        public void onAnimationEnd(Animator anim) {
            view.setAlpha(1f);
            view.setVisibility(visibility);

        }
    });
    animator.start();
}

  この中で、ObjectAnimatorを使ってVISIBLEになるときは、アルファの値を1へ、VISIBLE以外の状態(INVISIBLEかGONE)に変化する時はアルファ値を0へとアニメートさせることをやっています。
android:visibilityの代わりにbind:visibilityWithAnimationというタグを使って下のようにするとこのアニメーションが適用されます。

<Button
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:text="INVITE"
    android:onClick="@{() -> handler.onClickInvite(user)}"
    bind:font="@{`Roboto-Bold.ttf`}"
    bind:visibilityWithAnimation="@{user.isFriend ? View.GONE : View.VISIBLE}"
    />

BindingAdapterの先頭でViewのクラスやtagをチェックすることで特定のクラスや特定のtagのビューにだけアニメーションを適用することが可能です。

@BindingAdapter("bind:visibilityWithAnimation")
public static void setVisibility(final View view,
                                 final int visibility) {



    //特定のクラスにだけアニメーションを適用したり外したりする場合。
    if (view instanceof Button){

    }

    //特定のタグのビューだけアニメーションを変えたり、外したりする場合。
    if(view.getTag().equals("foo")){

    }

サンプル

サンプルプロジェクトはこちらからダウンロードできます。


まとめ

OnRebindCallbackを実装することで、Dataをバインドしたビューの再評価直前のタイミングにコールバックを得ることができます。
この時にTranslationManager.beginDelayedTransitionを使うことで、ビューの変化に簡単にアニメーションをつけることができます。
この方法を使わず、ビュー個別に自分独自のアニメーションをつけたい場合はBindingAdapterを使って実現することができます。

チュートリアルの内容を自分のプロジェクトに反映させる時にわからないことや問題にぶつかった時のために、Fourhandsではオンラインのメンタリングサービスを提供しています。
ビデオチャットやスクリーン共有をつかて実際にコードをメンターに見てもらいながら問題の解決や、考え方のアドバイスがもらえるサービスですので、是非活用して見てください。
fourhands.com