使用 Model View Presenter 模式时如何实现 RecyclerView?
How to implement a RecyclerView when using Model View Presenter pattern?
我是第一次使用 MVP,我相信我明白了,但我不确定 RecyclerView。据我所知,MVP 是关于使视图尽可能被动,以便所有业务逻辑都转到 Presenter,但是如何为 Recycler View 实现这一点?
到目前为止,这是我的代码:
合同
public interface PhotosContract {
// View
interface View {//: IBaseActivity {
fun showPhotos(photos: ArrayList<Photo>)
fun showText(message: String)
}
// Presenter
interface Presenter {//: IBasePresenter<View> {
fun getPhotos()
}
}
主持人
public class PhotosPresenter(var view: PhotosContract.View) :PhotosContract.Presenter {
var dataList = ArrayList<Photo>()
override fun getPhotos() {
//call for endpoint
val call : Call<ArrayList<Photo>> = ApiClient.getClient.getPhotos()
call.enqueue(object: Callback<ArrayList<Photo>> {
override fun onFailure(call: Call<ArrayList<Photo>>, t: Throwable) {
Log.d("FAIL","FAILED")
}
override fun onResponse(
call: Call<ArrayList<Photo>>,
response: Response<ArrayList<Photo>>
)
{
Log.d("SUCCESS","SUCCESSED")
dataList.addAll(response!!.body()!!)
Log.d("SIZELIST",dataList.size.toString())
view.showPhotos(dataList)
view.showText("SUCCESS")
}
})
}
}
RecyclerViewAdapter
class PhotosAdapter(private var dataList: List<Photo>, private val context: Context) : RecyclerView.Adapter<PhotosAdapter.PhotosViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotosAdapter.PhotosViewHolder {
return PhotosViewHolder(LayoutInflater.from(this.context).inflate(R.layout.list_item_home, parent, false))
}
override fun getItemCount(): Int {
return dataList.size
}
override fun onBindViewHolder(holder: PhotosAdapter.PhotosViewHolder, position: Int) {
val dataModel = dataList[position]
holder.titleTextView.text = dataModel.title
}
class PhotosViewHolder(itemLayoutView: View) : RecyclerView.ViewHolder(itemLayoutView){
var titleTextView: TextView = itemLayoutView.tv_title
}
}
Activity
class PhotosActivity : AppCompatActivity(),PhotosContract.View {
private lateinit var presenter: PhotosPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_photos)
presenter = PhotosPresenter(this)
presenter.getPhotos()
}
override fun showPhotos(photos: ArrayList<Photo>) {
photosRecyclerView.layoutManager = LinearLayoutManager(this)
photosRecyclerView.adapter = PhotosAdapter(photos,this)
photosRecyclerView.adapter?.notifyDataSetChanged()
}
override fun showText(message: String) {
Toast.makeText(this,message,Toast.LENGTH_LONG).show()
}
}
下面是我在使用 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)
}
}
适配器也很笨。当它需要绑定一个 item view holder 时,它会调用 presenter 来完成它。
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
}
// ...
}
这只是一种方法,我相信还有很多其他方法。我只是喜欢这个,因为所有的责任都属于演示者,其他任何地方都没有业务逻辑。
最终,您需要更改列表并通知适配器。为此,在您的 View
合同中添加几个方法,例如 notifyItemInserted(pos: Int)
并在演示者需要时调用它们。或者,更好的是,使用 DiffUtil
这样您就不必自己管理它了!
一旦你对 MVP 有了很好的理解,我强烈建议你转向 MVVM,因为它是 Google 推广的官方架构。大多数人也觉得它比 MVP 方便很多。
如果您有任何问题,请不要犹豫。
我是第一次使用 MVP,我相信我明白了,但我不确定 RecyclerView。据我所知,MVP 是关于使视图尽可能被动,以便所有业务逻辑都转到 Presenter,但是如何为 Recycler View 实现这一点?
到目前为止,这是我的代码:
合同
public interface PhotosContract {
// View
interface View {//: IBaseActivity {
fun showPhotos(photos: ArrayList<Photo>)
fun showText(message: String)
}
// Presenter
interface Presenter {//: IBasePresenter<View> {
fun getPhotos()
}
}
主持人
public class PhotosPresenter(var view: PhotosContract.View) :PhotosContract.Presenter {
var dataList = ArrayList<Photo>()
override fun getPhotos() {
//call for endpoint
val call : Call<ArrayList<Photo>> = ApiClient.getClient.getPhotos()
call.enqueue(object: Callback<ArrayList<Photo>> {
override fun onFailure(call: Call<ArrayList<Photo>>, t: Throwable) {
Log.d("FAIL","FAILED")
}
override fun onResponse(
call: Call<ArrayList<Photo>>,
response: Response<ArrayList<Photo>>
)
{
Log.d("SUCCESS","SUCCESSED")
dataList.addAll(response!!.body()!!)
Log.d("SIZELIST",dataList.size.toString())
view.showPhotos(dataList)
view.showText("SUCCESS")
}
})
}
}
RecyclerViewAdapter
class PhotosAdapter(private var dataList: List<Photo>, private val context: Context) : RecyclerView.Adapter<PhotosAdapter.PhotosViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotosAdapter.PhotosViewHolder {
return PhotosViewHolder(LayoutInflater.from(this.context).inflate(R.layout.list_item_home, parent, false))
}
override fun getItemCount(): Int {
return dataList.size
}
override fun onBindViewHolder(holder: PhotosAdapter.PhotosViewHolder, position: Int) {
val dataModel = dataList[position]
holder.titleTextView.text = dataModel.title
}
class PhotosViewHolder(itemLayoutView: View) : RecyclerView.ViewHolder(itemLayoutView){
var titleTextView: TextView = itemLayoutView.tv_title
}
}
Activity
class PhotosActivity : AppCompatActivity(),PhotosContract.View {
private lateinit var presenter: PhotosPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_photos)
presenter = PhotosPresenter(this)
presenter.getPhotos()
}
override fun showPhotos(photos: ArrayList<Photo>) {
photosRecyclerView.layoutManager = LinearLayoutManager(this)
photosRecyclerView.adapter = PhotosAdapter(photos,this)
photosRecyclerView.adapter?.notifyDataSetChanged()
}
override fun showText(message: String) {
Toast.makeText(this,message,Toast.LENGTH_LONG).show()
}
}
下面是我在使用 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)
}
}
适配器也很笨。当它需要绑定一个 item view holder 时,它会调用 presenter 来完成它。
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
}
// ...
}
这只是一种方法,我相信还有很多其他方法。我只是喜欢这个,因为所有的责任都属于演示者,其他任何地方都没有业务逻辑。
最终,您需要更改列表并通知适配器。为此,在您的 View
合同中添加几个方法,例如 notifyItemInserted(pos: Int)
并在演示者需要时调用它们。或者,更好的是,使用 DiffUtil
这样您就不必自己管理它了!
一旦你对 MVP 有了很好的理解,我强烈建议你转向 MVVM,因为它是 Google 推广的官方架构。大多数人也觉得它比 MVP 方便很多。
如果您有任何问题,请不要犹豫。