※2016/07/16 追記: これもう大分古いのでこれからAndroidやるなら最初からRecyclerViewからやったらいいんじゃないでしょーか
Lollipop周りについてあんまり興味が湧かないと思いつつも触らないわけにもいかないしな、という感じでボチボチRecyclerViewやCardViewを触っていたわけですが、RecyclerViewの違和感がすごかったので色々考えた結果「RecyclerViewはListViewの代替ではないよね」って事に気づきました。新しいListViewだと思ってたのがいけなかったんですね。とりあえず色々考えた結論について以下に列挙します。
結論
- RecyclerViewはListViewの代替ではない
- RecyclerViewはListViewの様な「沢山の子Viewの部分セットをrecycleしながら表示するView」という機能を分解したクラスセットである
- RecyclerViewはListViewでは解決出来ない or より自由にカスタマイズしたい場合に使う
- ListViewで済むならRecyclerViewはいらない
なんでRecyclerViewはListViewの代替ではないと思ったか
これはもう単純にListView用途でカジュアルに使おうと思ったらすごく使いにくかったから。LayoutManagerをセットするのはいいとして、各item間のdividerが無かったり、タッチフィードバックがなかったり、Adapterでは自分でitemの管理をしなければならなかったり、極めつけはOnItemClickListenerが無く自前で用意しなければならなかったりして、これはおかしくないかと思い始めた。まるでListViewを再発明しているかのようだ。
↑のRecyclerViewのOnItemClickListenerに関する質問の回答にもあるとおり、
Think of Recyclerview not as a ListView 1:1 replacement but rather as a more flexible component for complex use cases.
と言っていて全くその通りだなーと思った。
RecyclerViewのクラス群
RecyclerViewは以下のコンポーネントを提供している。多くがListViewが内部的に暗黙的に行っていた機能を個別に分解したもののように見える。Recyclerなんかまさにそうだし、ViewHolderは公式ドキュメントのHold View Objects in a View Holderで書かれているベストプラクティスで、今回正式にクラス化されたりしている。その他ListViewには無い機能を持つものもある。つまり新しいListViewを作ったのではなくて、ListViewに隠蔽されていた機能を分解して公開し自由に実装できる様にする事で「沢山の子Viewの部分セットをrecycleしながら表示するView」を柔軟に作成出来るようしたというのが正しいのではないかと思う。
なのでこの機能セットでListViewを再現する事は可能であるが、OnItemClickListenerといったitemに暗黙的にOnClickListenerを足す様な余計な機能は入っていないし、dividerやタッチフィードバックなども同様にItemDecorationを使ったりitemに適用するdrawableを自分で用意してねという事になる。
class | description |
---|---|
RecyclerView.Adapter<VH extends RecyclerView.ViewHolder> | RecyclerViewに表示するViewにアプリケーション固有のデータセットをバインドする機能を提供する |
RecyclerView.AdapterDataObserver | RecyclerView.Adapterの変化を監視するObserver |
RecyclerView.ItemAnimator | RecyclerView内のitem(View)が追加、変更、削除された時などにアニメーションを実行するクラス |
RecyclerView.ItemDecoration | itemの表示に対してデコレーションをおこなう(marginとかdividerとか) |
RecyclerView.LayoutManager | RecyclerView内にViewをどの様に配置するかの責任を持つクラス |
RecyclerView.LayoutParams | RecyclerView用のLayoutParams |
RecyclerView.OnItemTouchListener | Itemへのタッチイベントを処理するinterface |
RecyclerView.OnScrollListener | RecyclerViewのスクロールイベントを処理するinterface |
RecyclerView.RecycledViewPool | RecyclerView内で再利用するViewHolderをプールし、複数のRecyclerViewで共有する仕組みを提供するクラス |
RecyclerView.Recycler | Viewのリサイクルを管理するクラス |
RecyclerView.RecyclerListener | Viewのリサイクル時に通知を受け取る為のinterface |
RecyclerView.SmoothScroller | LayoutManagerで利用できるスクロール用のクラス |
RecyclerView.State | RecyclerViewの状態を管理するクラス |
RecyclerView.ViewCacheExtension | Viewをキャッシュする機構を拡張できるクラス。Recycler内で利用される |
RecyclerView.ViewHolder | itemを表示するViewを保持するクラス |
RecyclerViewの強力な所
じゃあどういう時にRecyclerViewを使うのかというと、既存のListViewでは解決出来なかったり、ListViewをイジって実現するにはコストが高い仕様を実装する時という事になるだろう。凝った事をしようとすれば割とすぐにListViewでは限界が来ると思うので最初っからRecyclerViewで行くのでもいい気はする。
RecyclerViewの強力な例としてLayoutManagerがある。LayoutManagerはRecyclerViewに表示するitemの表示方法やスクロールに関する管理をする。予め幾つかの実装が用意されている。今のところ以下の3つ。
- LinearLayoutManager
- GridLayoutManager
- StaggeredGridLayoutManager
これらはRecyclerView.setLayoutManager()
でセットするだけで使える。以下の画像は同じAdapterに対してLayoutManagerを入れ替えた例。
LinearLayoutManager | GridLayoutManager | StaggeredGridLayoutManager |
---|---|---|
カスタムLayoutManagerを作る事も出来る。これらはRecyclerViewやその他のコンポーネントとは疎になっていて管理がし易い。他のコンポーネント群についても同様で、色々な実装をRecyclerViewの統一的なAPIに乗せる事が出来るという点は強力だと思う。例えばGitHub - etsy/AndroidStaggeredGrid: An Android staggered grid view which supports multiple columns with rows of varying sizes.の様に新しいListViewをフルスクラッチするのでは無くRecyclerViewの体系に追加する方式を取れる。ライブラリ開発者的にも嬉しいんじゃないかなーと思う。
RecyclerViewのしんどい所
色んな所を自前で用意しなければならない点がRecyclerViewのしんどい所。ただむき出しながら結構キレイに分割されたAPIなので、最初のコストさえ払ってしまえば大した事はないかもなと思う。ただその設計が正しいのかどうかについてある程度検討が要るだろうし、将来に渡って複雑化しなさそうなものについてはListViewでやるという判断もあるだろう。
まとめ
冒頭の結論と同じ
- RecyclerViewはListViewの代替ではない
- RecyclerViewはListViewの様な「沢山の子Viewの部分セットをrecycleしながら表示するView」という機能を分解したクラスセットである
- RecyclerViewはListViewでは解決出来ない or より自由にカスタマイズしたい場合に使う
- ListViewで済むならRecyclerViewはいらない
なんというか、RecyclerView.ArrayAdapter<T>とかListViewLayoutManagerとか用意してくれてたらよかったんじゃないかと思った。そうすればListViewの代わりにカジュアルに利用しつつ追々発生した複雑な要件に柔軟に対応も出来るのになー。squareがいい感じのライブラリを出すのを待つというのも手かもしれない。