概要
MVPアーキテクチャでRecyclerViewを使用したときに、
データ管理の方法に悩みました。
そのときの対応方法を書きます。
背景
RecyclerViewを使用するときに、
RecyclerView.Adapter内でデータを保持する実装が一般的だと思います。
ただ、RecyclerView.Adapterはクラス名からもわかるように、Viewに属するクラスです。
MVPで言えば、Vです。
VにMを持たせて操作させたくありません。
PがMを持ってVの更新をしたいです。
それをするために少し奮闘しました。
どうやって乗り切ったのか
RecyclerView.Adapterの実装
ほぼ空っぽに。
class SampleListAdapter(
private val contentManager: ContentManager,
private val contentViewCreator: ContentViewCreator,
private val bindListener: OnBindListener
) : RecyclerView.Adapter<ViewHolder>() {
interface ContentManager {
fun getItemCount(): Int
fun getItemViewType(position: Int): Int
}
interface ContentViewCreator {
fun createContentView(context: Context): SampleContentView
fun createCampaignContentView(context: Context): CampaignContentView
}
interface OnBindListener {
fun onBindContentView(holder: SampleViewHolder, position: Int)
fun onBindCampaignContentView(holder: CampaignViewHolder, position: Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
when (findByViewType(viewType)) {
default -> SampleViewHolder(contentViewCreator.createContentView(parent.context))
}
override fun getItemCount(): Int = contentManager.getItemCount()
override fun getItemViewType(position: Int) = contentManager.getItemViewType(position)
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
when (holder) {
is SampleViewHolder -> bindListener.onBindContentView(holder, position)
else -> throw IllegalArgumentException()
}
}
Viewの実装
ちょっとボリューミーだけど、こんな感じ。
interface SampleView : BaseView {
fun notifyData()
fun setDataCount(count: Int)
fun onBindContentViewHolder(holder: SampleViewHolder, model: SampleModel, position: Int)
}
class SampleFragment : Fragment, SampleView {
fun setAdapter() {
recyclerView.adapter = SampleListAdapter(
object : SampleListAdapter.ContentManager {
override fun getItemCount(): Int {
return presenter.getItemCount()
}
override fun getItemViewType(position: Int): Int {
return presenter.getViewType(position)
}
},
object : SampleListAdapter.ContentViewCreator {
override fun createContentView(context: Context): SampleContentView {
return SampleContentView(context)
}
},
object : SampleListAdapter.OnBindListener {
override fun onBindContentView(holder: SampleViewHolder, position: Int) {
presenter.onBindContentView(holder, position)
}
}
)
}
}
Presenterの実装
データが必要なところだけ、Presenterに返すようにする。
PresenterにViewHolderへの参照が入ってしまうのは致し方なし...
getter追加も苦肉の策...
interface SamplePresenter : BasePresenter<SampleView> {
fun getItemCount(): Int
fun getViewType(position: Int): Int
fun onBindContentView(viewHolder: SampleViewHolder, position: Int)
}
終わりに
若干の無茶はしつつも..
目的の PがMを持ってVの更新をしたい がRecylerViewでできました。
結構ざっくりとしかコードを載せていので、
これだけを見ると意味がわからない人も多くいる気がします。
もし要望が多ければ、サンプルプロジェクトを作って
GitHub にアップするかも..しないかも..しれないので、コメントいただければと。