visible true

技術的なメモを書く

RecyclerViewはListViewの代替ではないよねという話

※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を再発明しているかのようだ。

java - Why doesn't RecyclerView have onItemClickListener()? And how RecyclerView is different from Listview? - Stack Overflow

↑の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がいい感じのライブラリを出すのを待つというのも手かもしれない。