I am learning MVP and got confused where and how should I implement onClickListener while not ruining mvp concept here.
Followed this 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's
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 guides I saw was about creating onClickListener object in Adapter and then use it in ViewHolder, but in this implementation, I override all adapter function in my presenter and passing onClickListener (android related stuff) would contradict mvp pattern. What to do in this case. Maybe someone could write a solution - really confused.
My main goal would be to click a recyclerview item and get item name (via toast)
Instead of calling the presenter inside your adapter, I would rather make an interface of the click to call it from the view, since you will instantiate this adapter in your view, it's a good thing to keep the MVP pattern with the click of the elements inside your view and not in the adapter itself.
This example is in Kotlin, but I'm sure you will understand it.
First, just make a simple interface to call your click event whenever 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 should 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)
}
}
Lastly, just implement the interface of the click in the view that you are calling the adapter, and then just manage the presenter interactions there instead inside the adapter
class EquipmentActivity : BaseActivity(), EquipmentContract.EquipmentView, EquipmentAdapter.RecyclerViewClickListener ...
And implement the click method
override fun recyclerViewListClicked(v: View?, position: Int) {
presenter.onItemInteraction(position)
}
Doing this, you are making sure that the click of the elements in the list are being made from the view itself and not from the adapter, here, you can interact with the presenter as always and also do more things that will keep your project clean.
OnClickListener is an interface from Android SDK. Your presenter should not know anything about the Andriod SDK. It should be pure Java so it can be tested just by using Unit test on the JVM. It shouldn't know anything about views, RecyclerView, Adapter nor ViewHolder.
Your onBindViewHolder doesn't violate this principle because it's separated by an abstract interface - RepositoryRowView.
You should implement OnClickListener in adapter/viewholder 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());
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With