RecyclerView.ItemAnimatorでListItemにアニメーションをかけてみる

Android 5.0 が出てしまいましたね…
ApiLevelが21になって本当に良かった… Lのままだったらどうしようって不安だったのは他にもいたはずｗ


適当にAndroidアプリ作っていると、まあListViewからは逃げられません。
そしてそんなこんなで [EnhancedListView](https://github.com/timroes/EnhancedListView)もDeprecatedに…

というわけで試しにRecyclerViewを触ってみました。

今回落ちは最後に書いてますので、お急ぎの方は一番最後からお読みください

## とりあえず動かすだけ

動かすだけなら

* `RecyclerView.Adapter`を継承したAdapter作る
* RecyclerViewにそのAdapterをセットする
* あとLayoutManagerなるものもセットする

だけで使えます

今まで暗黙的に（？）ViewHolderを作っていたと思いますが、
なんか強制されるようになったぐらいなイメージです

ググればすぐ出てくるんでこの辺は割愛

ただ使うだけなら楽です

## 個々の要素にアニメーションをかける

`RecyclerView#setItemAnimator` で `RecyclerView.ItemAnimator` を継承したクラスを突っ込むと

* 要素の追加
* 要素の削除
* 要素の移動
* 要素の変更

の時にアニメーションをかけることが可能です

### ItemAnimatorを実装してみ…

なんと **たった8個のメソッドを実装するだけ** で良いのです

…

しかもちょっとしたことを気にしておけばOK！？

**アニメーション開始時とか終了時とかに特定のメソッドを呼ぶ必要がちょっとあるだけ！**

…


#### 実装すべきメソッド紹介

* runPendingAnimations
* animateRemove ※1
* animateAdd ※1
* animateMove ※1
* animateChang ※1
* endAnimation
* endAnimations
* isRunning

まあ `extends RecyclerView.ItemAnimator` とかやればIDEが勝手にひな形作ってくれますね

#### 超ざっくりとした流れ

1. RecyclerViewにItemAnimatorをセットする
  * changeアニメーションを扱う場合は`ItemAnimator#setSupportsChangeAnimations(true)` を忘れずに
2. Adapterでそれぞれのイベントにあった通知を呼ぶ
  * notifyItemInserted
  * notifyItemRangeInserted
  * notifyItemRemoved
  * notifyItemRangeRemoved
  * notifyItemMoved
  * notifyItemChanged
  * notifyItemRagenChanged
3. animate…(※1のやつら) にイベントが降ってくる
  * 適当に保存しましょう
  * true返しましょう
  * アニメーション開始状態はここで作っておきましょう
4. runPendingAnimationsが呼ばれる
  * 3で保存したイベントを消化しましょう
  * 3でtrueを返した数だけイベントが発生する **「わけではない」** ので、保存したイベントは **すべて消化** しときましょう
  * Add/Remove/Move/Change それぞれのアニメーション開始終了時に、それぞれ適したメソッド呼びましょう
5. endAnimationsが呼ばれる
  * なんかあったら呼ばれるからアニメーションキャンセルとか実装しておけよ！

です

だるっ

#### animate... 系のメソッドに関して

注意点は

* 対象ViewのAnimation開始状態はここで決めておくこと

だと思います

例えばAddでフェードインしたければ、引数として飛んでくる`ViewHolder`の`itemView`(直接参照なのがまたすごいな…)のAlphaを0にするとか

ぶっちゃけ詳しく調べてないんですけど、メソッド内で全部Animation処理してfalse返すってのでも行けるかもしれませんが…
まーたぶんやめたほうが良いと思います、試してないけど。
それでいいならrunPendingAnimationsいらな…


#### runPendingAnimations

実際にAnimationをやる場所です

この時どういう順でAnimation処理するかも考えておきましょう

だって削除したら消えた部分は下のやつがせり上がってきたりするでしょ？

ってことは削除より前に移動アニメーション実行すると…

まあ見せ方考える必要ありますね

また注意点としてアニメーション開始時にアニメーションのタイプ(追加とか削除とか）に合わせて

* dispatchAddStarting
* dispatchRemoveStarting
* dispatchMoveStarting
* dispatchChangeStarting

を呼び、終了時もタイプに合わせて

* dispatchAddFinished
* dispatchRemoveFinished
* dispatchMoveFinished
* dispatchChangeFinished

を呼びましょう

更に、すべてのアニメーションが終了した時に

* dispatchAnimationsFinished

を呼ぶのを忘れ…

あとはendAnimet…

#### あ、もう説明するのだるい、コード読んで

そもそものDefaultのAnimationは`DefaultItemAnimator`という形で存在します

$ANDROID_HOME/extras/android/m2repository/com/android/support/recyclerview-v7/21.0.0/recyclerview-v7-21.0.0-sources.jar

の中にあるんで。

RecyclerViewは8000行以上ありますけど、これは800行くらいだから…

だから…


## 辛い

そもそもさ、Animationのこと**だけ**実装して渡せば、
**後のことはよしなにやってくれる！**とかがいいなぁ…
と思うんですよ？

超複雑なことやるならともかく…


## これなら辛くない！(主に俺が)

なので[実装](https://github.com/daneko/SimpleItemAnimator)しました

[どうぞご利用ください](https://github.com/daneko/SimpleItemAnimator)

こちらの製品ではなんとあなたはAnimationのことだけ考えればOKです

```java
recyclerView.setItemAnimator( SimpleItemAnimator.builder().
    preAddAnimationState(v -> {
        ViewCompat.setAlpha(v, 0);
        return Unit.unit();
    }).
    addAnimation(animator -> animator.alpha(1)).
    addDuration(500).
    removeAnimation(animator ->
            animator.translationXBy(recyclerView.getWidth())).
    preChangeAnimationState((oldV, newVOpt, param) -> {
        ViewCompat.setAlpha(oldV, 1);
        newVOpt.foreach(newV -> {
            ViewCompat.setAlpha(newV, 0);
            ViewCompat.setRotationX(newV, -180);
            return Unit.unit();
        });
        return Unit.unit();
    }).
    changeAnimation(
            (forOld, param) -> forOld.rotationX(180).alpha(0),
            (forNew, param) -> forNew.rotationX(0).alpha(1)).
    isChangeAnimationMix(false).
    changeDuration(500).
    build());
```

なんとたったこれだけで

* 追加時は500msでフェードイン
* 削除時はdefault設定(120ms)で画面右に吹っ飛んで消える
* 変更時は古いViewが500msかけて回転して消えた後に、新しいViewが500msかけて回転して現れる

という設定が可能です!

動いているサンプル全容を知りたい場合は[こちらのsampleをそのままBuildしてみてください](https://github.com/daneko/SimpleItemAnimator/blob/master/sample)

なお、何も設定しなくてもDefaultItemAnimatorと同様の動きをする**はず**です
そのへんは[この辺](https://github.com/daneko/SimpleItemAnimator/blob/da2466d70cab852d77ffc95c5c3c3ff1479bc953/library/src/main/java/com/github/daneko/simpleitemanimator/DefaultAnimations.java)を…

ぇ？

RxAndroidとかFunctionalJavaついてきて困る？
しかもRetrolambda？？

大丈夫、sampleにproguardの設定つけといたから(震え声

ぇ？

テスト無い？

いやだってUI周りでAndroid依存しまくりでもうめんど…


うん、まあ…

どう見ても完全に俺得ライブラリです、本当にありがとうございました

これらの依存関係の無い素敵なライブラリ誰か作ってください

もしくは次のupdateとかでそんな感じになってないかなぁ…