Implement onItemClickListener using the MVP pattern

I am learning MVP and confused where and how I should implement onClickListener without breaking the concept of mvp here.

Next Guide: https://android.jlelse.eu/recyclerview-in-mvp-passive-views-approach-8dd74633158

My implementation.

Adapter:

 public class RepositoriesRecyclerAdapter extends RecyclerView.Adapter<RepositoriesRecyclerAdapter.RepoViewHolder> { private final RepositoriesListPresenter presenter; public RepositoriesRecyclerAdapter(RepositoriesListPresenter repositoriesPresenter) { this.presenter = repositoriesPresenter; } @Override public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new RepositoryViewHolder(LayoutInflater.from(parent.getContext()) .inflate(R.layout.cell_repo_view, parent, false)); } @Override public void onBindViewHolder(RepositoryViewHolder holder, int position) { presenter.onBindRepositoryRowViewAtPosition(position, holder); } @Override public int getItemCount() { return presenter.getRepositoriesRowsCount(); } } 

RepositoryViewHolder

 public class RepositoryViewHolder extends RecyclerView.ViewHolder implements RepositoryRowView { TextView titleTextView; TextView starsCountTextView; public RepositoryViewHolder(View itemView) { super(itemView); titleTextView = itemView.findViewById(R.id.repoTitleText); starsCountTextView = itemView.findViewById(R.id.repoStarsCountText); } @Override public void setTitle(String title) { titleTextView.setText(title); } @Override public void setStarCount(int starCount) { starsCountTextView.setText(String.format("%s ★", starCount)); } } 

RepositoryRowView

 interface RepositoryRowView { void setTitle(String title); void setStarCount(int starCount); } 

All the tutorials that I saw related to creating the onClickListener object in the Adapter and then using it in the ViewHolder, but in this implementation, I override the entire adapter function in my presenter and passing the onClickListener (Android related) stuff contradicts the mvp pattern. What to do in this case. Maybe someone can write a solution - really confusing.

My main goal is to click the recyclerview element and get the element name (via toast)

+9
source share
3 answers

OnClickListener is the interface from the Android SDK. Your host should not know anything about the Andriod SDK. It must be pure Java, so it can only be tested with Unit test in the JVM. It should not know anything about views, RecyclerView, Adapter, and ViewHolder.

Your onBindViewHolder does not violate this principle, because it is separated by an abstract interface - RepositoryRowView.

You must implement OnClickListener in the adapter / viewer and call your presenter from there.

 public class RepositoryViewHolder extends RecyclerView.ViewHolder implements RepositoryRowView, View.OnClickListener { TextView titleTextView; TextView starsCountTextView; RepositoriesListPresenter presenter; public RepositoryViewHolder(View itemView, RepositoriesListPresenter presetner) { super(itemView); titleTextView = itemView.findViewById(R.id.repoTitleText); starsCountTextView = itemView.findViewById(R.id.repoStarsCountText); this.presenter = presenter; itemView.setOnClickListener(this); } @Override public void setTitle(String title) { titleTextView.setText(title); } @Override public void setStarCount(int starCount) { starsCountTextView.setText(String.format("%s ★", starCount)); } @Override public void onClick(View view) { if (presenter != null) { presenter.onItemInteraction(getAdapterPosition()); } } } 
+11
source

Instead of invoking a presenter inside your adapter, I would prefer to make a click interface to invoke it from the view, since you will instantiate this adapter in your view, it would be nice to save the MVP template by clicking the elements inside your view, and not in the adapter itself.

This example is in Kotlin, but I'm sure you will understand this.

First, just create a simple interface to trigger a click event when the user clicks on any item in your list.

 class EquipmentAdapter(private val context: Context,private var equipmentList:ArrayList<Equipment>,itemListener:RecyclerViewClickListener): RecyclerView.Adapter<EquipmentAdapter.EquipmentViewHolder>() { interface RecyclerViewClickListener { fun recyclerViewListClicked(v: View?, position: Int) } companion object{ var itemClickListener: RecyclerViewClickListener? = null var equipmentSearchList:ArrayList<Equipment>? = null } init { equipmentSearchList = equipmentList itemClickListener = itemListener } 

Then inside your ViewHolder you must call this interface to handle the click

 inner class EquipmentViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener { val equipmentName:TextView = itemView.txt_equipmentname init { itemView.setOnClickListener(this) } override fun onClick(v: View?) { itemClickListener?.recyclerViewListClicked(v, adapterPosition) } } 

Finally, just implement the click interface in the view that you call the adapter, and then simply control the speaker interactions inside the adapter instead.

 class EquipmentActivity : BaseActivity(), EquipmentContract.EquipmentView, EquipmentAdapter.RecyclerViewClickListener ... 

And implement the click method

 override fun recyclerViewListClicked(v: View?, position: Int) { presenter.onItemInteraction(position) } 

By doing this, you check that the clicks on the elements in the list are made from the view itself, and not from the adapter, here you can interact with the speaker as always, as well as do more things that will keep your project clean,

+1
source

It is interesting. I have a similar situation where I need to catch an element and change its appearance. (I decided to ask a question here because another ppl might also be needed) @JosefAdamcik A good example of how to control a click from an abstraction ( Viewholder to Presenter ), but what if the click is set to Adapter ? I can notify Presenter from my Adapter (passing the position of a clicked element), but I cannot use the abstraction mentioned above.

0
source

Source: https://habr.com/ru/post/1271392/


All Articles