【Andorid】スクロールにあわせて複数のViewを連動して操作する


# まえがき

ネイティブアプリを作る際、スクロール周りの動きがよく出来ているとアプリの質が高く感じられます。
例えばRecyclerView（スクロールに用いられるView）を下に向かってスクロールしたらヘッダーが上に移動して消え、下からボタンが出てくる、といった動きなどがよく見受けられます。

こういう動きをフランクに実装したいと思っても、リスナーを設定してスクロール状況から他のViewの位置を計算して、、、となってしまい、意外とめんどくさく、連動するViewの数が増えるとさらに大変だったりします。
さらに、最近のアプリでは複数の画面でこういった実装が必要になってきてなんとかうまく共通化したいのだが、なかなかうまくいかなかったり。



# 便利なライブラリ
ここではざっくりとしか取り扱いませんが、とりあえず2つほどあげさせていただきます。

### Android-ObservableScrollView

[Android-ObservableScrollView](https://github.com/ksoichiro/Android-ObservableScrollView)

[ksoichiro](https://github.com/ksoichiro)さんによる有名なライブラリ。
スクロールやタッチイベントを処理して、適切なコールバックに振り分けて通知してくれる。
実用的なサンプルも多く用意されており、非常に参考にさせていただいています。



### CoordinatorLayout

この間Androidのsupport libraryに加えられたコンポーネント。
FloatingActionButtonとsnackbarの例が有名で、複数のViewが影響しあうような場面で使うクラスで、スクロールによる動きに対応した機能もあります。

[この記事](https://guides.codepath.com/android/Handling-Scrolls-with-CoordinatorLayout)はCoodinatoraLayoutを用いたリッチなスクロールアニメーションの概要を把握するいい記事だと思います。
英語ですが、デモやサンプルソースが多くて比較的わかりやすいと思います。
ただし、このライブラリはまだ完成度が高くなく挙動が安定しないことが多々あります。
特にリストをスワイプでスクロールした際の挙動がカクカクしてたりするので、自分はまだ使わないようにしています。




# ライブラリを作ってみました
[Disco](https://github.com/m1noon/Disco)というライブラリを作ってみました。
まだあまりこみいって作っていないので恐縮ですが紹介させていただきます。
ディスコに集ったViewたちに、それぞれのChoreography（振り付け）を設定してあげればみんな踊り出す、という80年代的コンセプトの愉快な内容です。笑

### 特徴
「あるViewのスクロールの動きに連動してViewを動かす」ことが目的です。
スクロールしたらヘッダーが消える、ボタンが表示されるといった難しくはないが煩雑なコードになりがちな部分をシンプルに記述できます。
先ほどあげさせていただいた[Android-ObservableScrollView](https://github.com/ksoichiro/Android-ObservableScrollView)のように複雑なカスタマイズはできませんが、コードが簡潔になることを目的としています。


### デモ
サンプルアプリを動かした様子です。

![demo_complex.gif](https://qiita-image-store.s3.amazonaws.com/0/58726/aae4343d-12b2-84c4-935a-311e4a7a39f5.gif "demo_complex.gif")



### 使い方
DiscoというクラスにスクロールするViewと、それに連動するView、さらには連動するViewに連動して動くViewなどを追加していきます。
以下のように書くと、後は自動で動き出してくれます。


``` 
Disco disco = new Disco();

disco.addScrollObserver(mHeaderImage, disco.getScrollChoreographyBuilder()
                        .onScroll()
                        .offset(100)
                        .multiplier(0.7f)
                        .build()
        );

// タイトルのテキストがスクロールに合わせて左にスケールしながら動く
disco.addScrollObserver(mTitleText, disco.getScrollChoreographyBuilder()
                        .onScroll()
                        .stopAtBorder()
                        .topOffset(getResources().getDimensionPixelOffset(R.dimen.4dp))
                        .scaleX(1f, 0.6f)
                        .scaleY(1f, 0.6f)
                        .translationX(Position.DEFAULT, 0,
                        	 Position.LEFT,getResources().getDimensionPixelOffset(R.dimen.4dp))
                        .build()
        );
        

// Fab(FloatingActionButton)がスクロールする
disco.addScrollObserver(mFab, disco.getScrollChoreographyBuilder()
                        .onScroll()
                        .topOffset(dpToPixcel(getActivity(), 90))
                        .build()
        );

// Fabが自分の移動位置に合わせてスケールアニメーションで出たり消えたりする
disco.addViewObserver(mFab, mFab, disco.getViewChaseChoreographyBuilder()
                        .atTag(ViewParam.TRANSLATION_Y, dpToPixcel(getActivity(), -150))
                        .scaleX(0, 1)
                        .scaleY(0, 1)
                        .duration(200)
                        .interpolator(new DecelerateInterpolator())
                        .notifyEvent(SampleEvent.BACK, SampleEvent.FORWARD)
                        .build()
        );
```

### 終わり
内容が薄めで申し訳ありませんが、以上で終わりとなります。
Qiita、Githubなどでレスポンスいただけるとありがたいです。
まだまだ未熟者ですが、精進していきたいと思います。

minSDKVersionが高かったり、まだjcenterに登録されていなかったり、といった細かい部分は早めに修正します（汗）


[demo_complex]:https://github.com/m1noon/Disco/blob/master/art/demo_complex.gif