Вот как я это сделал, когда использовал MVP. В вашем контракте определите дополнительное представление с именем ItemView
. Как я это делаю, каждый держатель представления элементов является представлением MVP. Вид тупой, поэтому он просто вызывает докладчика всякий раз, когда что-то происходит, а докладчик вызывает его обратно.
interface MyContract {
interface View {
fun setTitle(title: String)
}
// Add this interface here
interface ItemView {
fun bindItem(item: Item)
}
interface Presenter {
fun attach(view: View)
fun detach()
val itemCount: Int
fun onItemClicked(pos: Int)
fun onBindItemView(itemView: ItemView, pos: Int)
}
}
Адаптер также не работает. Когда ему необходимо привязать держатель представления элемента, он вызывает для этого ведущего.
class MyAdapter : RecyclerView.Adapter<ViewHolder>() {
// How many items do we have? We don't know, ask the presenter.
override fun getItemCount() = presenter?.itemCount ?: 0
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// How to bind the item if we only have position? We don't know, ask the presenter.
presenter?.onBindItemView(holder, position)
}
// ...
}
ViewHolder
реализует интерфейс MyContract.ItemView
. Опять же, это просто взгляд, поэтому он не несет никакой ответственности. Он просто делегирует полномочия докладчику.
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), MyContract.ItemView {
private val txv: TextView = view.findViewById(R.id.text_view)
init {
view.setOnClickListener {
// What to do here, we only have the item's position? Call the presenter.
presenter?.onItemClicked(adapterPosition)
}
}
override fun bindItem(item: Item) {
txv.text = item.text
}
}
И, наконец, докладчик:
class MyPresenter : MyContract.Presenter {
private var view: View? = null
private val items = mutableListOf<Item>()
override fun attach(view: View) {
this.view = view
// ...
}
override fun detach() {
view = null
}
override val itemCount: Int
get() = items.size
override fun onItemClicked(pos: Int) {
val item = items[pos]
// ...
}
override fun onBindItemView(itemView: ItemView, pos: Int) {
itemView.bindItem(items[pos])
}
// ...
}
Вид для полноты, но здесь ничего нового:
class MyView : Fragment(), MyContract.View {
private var presenter: Presenter? = null
override fun onViewCreated(view: View) {
// Attach presenter
presenter = MyPresenter()
presenter?.attach(this)
}
override fun onDestroyView() {
super.onDestroyView()
// Detach the presenter
presenter?.detach()
presenter = null
}
// ...
}
Это только один способ сделать это, я уверен, что есть много других. Мне просто нравится этот, потому что вся ответственность лежит на докладчике, здесь нет бизнес-логов c где-либо еще.
В конце концов, вы захотите внести изменения в свой список и уведомить адаптер. Для этого добавьте в свой контракт View
пару методов, например notifyItemInserted(pos: Int)
, и при необходимости вызовите их из докладчика. Или, еще лучше, используйте DiffUtil
, чтобы вам не приходилось управлять им самостоятельно!
Однако, когда вы хорошо разбираетесь в MVP, я настоятельно рекомендую вам перейти на MVVM, поскольку это официальная архитектура, продвигаемая от Google. Большинство людей находят его намного удобнее, чем MVP.
Если у вас есть вопросы, не стесняйтесь.