StickyListHeaders風のライブラリを自作する-3
StickyListHeaders風のライブラリを自作する-2 - 日々是好日の続き。
PinningListAdapter
について考えてみる。
リポジトリ作りました。 github.com
PinningListAdapter
今作っている Sticky なリストビュー用のアダプタ。
データセットの型とか ViewHolder
を柔軟にするため抽象クラスで定義している。
コンストラクタでデータセットを渡す是非は考えなければならない…。
abstract class PinningListAdapter<E, VH : RecyclerView.ViewHolder>(private val list: List<E>) : RecyclerView.Adapter<VH>(), PinningListDecoration.PinningListListener { //HEADER の目印となる定数。viewTypeの仕分けに使用 companion object Constants { const val HEADER = 200 } //getItemViewType は具象クラスで実装 override fun isHeader(adapterPosition: Int): Boolean { return getItemViewType(adapterPosition) == HEADER } //ヘッダーに当たるまでさかのぼり override fun getCurrentHeaderPosition(adapterPosition: Int): Int? { var index = adapterPosition while (index > -1) { if (isHeader(index)) return index index-- } return null } override fun getItemCount(): Int { return list.size } } //PinningListListener はこれ interface PinningListListener { val headerLayout: Int? fun isHeader(adapterPosition: Int): Boolean fun getCurrentHeaderPosition(adapterPosition: Int): Int? fun bindHeaderData(header: View, adapterPosition: Int) }
利用する場合の具象クラスでは次のように実装。
必ず override しなければならないメソッド(headerLayout
のみ変数)は次のとおり。
RecyclerView
のメソッドは普段でもほぼ必ず override するやつですね。
- RecyclerView#onCreateViewHolder
- RecyclerView#onBindViewHolder
- RecyclerView#getItemViewType
- PinningListListener#bindHeaderData
- PinningListListener#headerLayout: Int?
- 一応ヘッダーが無くても使えるように null 許容
class MyPinningListAdapter(private val list: List<Int>, override val headerLayout: Int?) : PinningListAdapter<Int, RecyclerView.ViewHolder>(list) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) //viewType で展開する ViewHolder を仕分け return when { viewType == HEADER && headerLayout != null -> {...} else -> {...} } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, adapterPosition: Int) { //実装した ViewHolder によりバインドの処理を仕分け when (holder) { is HeaderViewHolder -> {...} is ItemViewHolder -> {...} else -> {...} } } override fun getItemViewType(adapterPosition: Int): Int { //基本は list[adapterPosition] 等によりリストのデータが //ヘッダーかどうかを判定し、viewType を返す return when (...) { 0 -> HEADER else -> super.getItemViewType(adapterPosition) } } //PinningListDecorationにおいて、ヘッダーだった場合のみ呼ばれるので、 //View はヘッダー決め打ちでおk override fun bindHeaderData(header: View, adapterPosition: Int) { header.header_title.text = "HEADER: " + list[adapterPosition].toString() } //ヘッダー用とアイテム用とで ViewHolder を複数定義 class ItemViewHolder(view: View): RecyclerView.ViewHolder(view) {...} class HeaderViewHolder(view: View): RecyclerView.ViewHolder(view) {...} }
課題
- 複数の ViewHolder(上記+フッター等) を定義するとき、viewType での仕分けがどんどん膨らんでしまう
- ViewHolder にインターフェース実装して、条件分岐を減らしてみたい
RecyclerView ぜんぜんわからん。