マテリアルデザインのFAB(Floating Action Button)のアニメーションをパクる

この記事はAndroidアプリケーション(Java)開発向け記事です。

アニメーションをしているコードだけをみたいかたは"[FABのshowアニメーションのみをパクる](#fab%E3%81%AEshow%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E3%81%BF%E3%82%92%E3%83%91%E3%82%AF%E3%82%8B)"へどうぞ。

## 2016年5月現在のMaterial Designについて(雑に)
<blockquote class="twitter-tweet" lang="ja"><p>Did you hear?
There's now over *1 million* #materialdesign apps in the @GooglePlay store! 
(extend your pinky and spread the word!) #io16</p>&mdash; Matias Duarte (@MatiasDuarte_) <a href="https://twitter.com/MatiasDuarte/status/733697565967941633">0:35 - 2016年5月21日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

上記記事にあるように、もうすでにマテリアルデザインを導入したアプリは100万を超えたらしいです。
今後は似たようなUIが多くなり、アプリの個性が薄くなってしまう問題にぶち当たる事もあるかもしれないです。そういう場合は成功例を見て差別化をどのようにやっているのかを、研究してみるのがいいかもしれませんね。

そういうわけで差別化として「MaterialDesignなアニメーションを使ったインタラクションを入れたい！」って人向けの記事になりますかね。


## 一番簡単にMaterialDesignなアニメーションを確認する方法
下記のリンクにあるFABアニメーションが今回のターゲットです。
[アニメーション動画へのリンク](https://material-design.storage.googleapis.com/publish/material_v_8/material_ext_publish/0B14F_FSUCc01UlJKVG9BVVFuMHM/GoodTransition_ClearDo-v3.webm)

１、とりあえず試したい場合は適当にプロジェクトを作成(minSdkVersion 14(Android 4.0以上))。
Activityをメニューから
`"File -> New -> Activity -> Scrolling Activity"`
でMainActivityの遷移先のActivityを作成します。ここでは名前を仮にSubActivityとしておきます。
(勝手にサポートライブラリーのdependenciesが追加されるので、そこらへんは気にしなくていいと思います。)

そうすると自動的にToolbarとFABが配置されたレイアウトが作成され、SubActivityクラスでは、スクロールする中身の実装やToolbarのカスタマイズなどをすればいいだけの状態のActivityが作成されます。

２、次はSubActivityのFABに遷移したときに動くアニメーションを入れたいと思います。
ここで、一々ゴリゴリAnimationを定義する必要はなく、FloatingActionButtonクラスにshow()というメソッドが定義されているので、そちらを呼び出せばいいです。（onResumeでshowしてもアニメーションしないので注意）

コードとしてはonCreateでFABをfindViewByIdしている次の行から以下のコードを書きます。

```java:SubActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sub);
        final FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab);
        // VISBLE以外の状態を設定する
        fab.setVisibility(View.GONE);
        fab.getViewTreeObserver()
           .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                fab.show();
            }
        });
    }
```
注意点はshowする際はfabは可視(View.VISIBLE)の状態ではいけません。showされる前のFABのVisibilityはGONEかINVISIBLEにする必要があります。

これだけで、画面遷移した際にFABがひょこっと出てくるようになります。


## FABのshow時のアニメーションについて
show()のメソッド定義を見てもらえばわかりますけど、show()メソッド無いにアニメーションが直書きされています。こんな感じ↓。

```java:FloatingActionButtonIcs.java(android.support.design.widget)
 @Override
    void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
        if (mIsHiding || mView.getVisibility() != View.VISIBLE) {
            if (ViewCompat.isLaidOut(mView) && !mView.isInEditMode()) {
                mView.animate().cancel();
                if (mView.getVisibility() != View.VISIBLE) {
                    // If the view isn't visible currently, we'll animate it from a single pixel
                    mView.setAlpha(0f);
                    mView.setScaleY(0f);
                    mView.setScaleX(0f);
                }
                mView.animate()
                        .scaleX(1f)
                        .scaleY(1f)
                        .alpha(1f)
                        .setDuration(SHOW_HIDE_ANIM_DURATION)
                        .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
                        .setListener(new AnimatorListenerAdapter() {
                            @Override
                            public void onAnimationStart(Animator animation) {
                                mView.internalSetVisibility(View.VISIBLE, fromUser);
                            }

                            @Override
                            public void onAnimationEnd(Animator animation) {
                                if (listener != null) {
                                    listener.onShown();
                                }
                            }
                        });
            } else {
                mView.internalSetVisibility(View.VISIBLE, fromUser);
                mView.setAlpha(1f);
                mView.setScaleY(1f);
                mView.setScaleX(1f);
                if (listener != null) {
                    listener.onShown();
                }
            }
        }
    }
```
再利用できませんよね…。ケチ。

## FABのshowアニメーションのみをパクる
くどくど長くなってすみません、本題はこちらです。
上のコードそのまま使うのもいいんですけど、show()と同じに非表示にするときにhide()というメソッドもあるし、まぁ表示するアニメーションだけ欲しいし、全部書くのは面倒かなと思いますので、今回は最小限のパクリに抑えようと思います。

パクった結果が下記のコードです。

```java:AnimationUtil.java

    /** view animation defines **/
    public interface InternalVisibilityChangedListener {
        void onShown();
    }
    private static final int SHOW_HIDE_ANIM_DURATION = 200;
    private static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR =
            new LinearOutSlowInInterpolator();

    public static void show(final View view, final InternalVisibilityChangedListener listener) {
        if (view.getVisibility() == View.VISIBLE) return;

        view.animate().cancel();

        view.setAlpha(0f);
        view.setScaleY(0f);
        view.setScaleX(0f);

        view.animate()
            .scaleX(1f)
            .scaleY(1f)
            .alpha(1f)
            .setDuration(SHOW_HIDE_ANIM_DURATION)
            .setInterpolator(LINEAR_OUT_SLOW_IN_INTERPOLATOR)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    view.setVisibility(View.VISIBLE);
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    if(listener!= null) listener.onShown();
                    super.onAnimationEnd(animation);
                }
            });

    }
```

これで、他のViewでも同様にひょこっとスケールしてくるアニメーションを表現できるようになりました。

皆さまのお手伝いになれば光栄です。

以上が今回の記事の内容になります。