OnClickListener for RecyclerView
Solution 1
Instead of setting onclicklistener for the entire RecyclerView
set it inside the constructor of the ViewHolder
class of your Adapter.
Some sample code will be like the following class which is a inner class inside the recyclerview adapter.
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
.....
public ViewHolder(View itemView) {
super(itemView);
.......
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
.......
}
}
Inside onCreateViewHolder
of your adapter , pass the inflated view to the constructor of ViewGroup
something like ,
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.your_layout, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
Solution 2
While there are already some answers, but I thought I might provide my implementation of it as well with an explanation. (Full details can be found on another similar question I answered).
So, to add a click listener, your inner ViewHolder
class needs to implement View.OnClickListener
. This is because you will set an OnClickListener
to the itemView
parameter of the ViewHolder
's constructor. Let me show you what I mean:
public class ExampleClickViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView text1, text2;
ExampleClickViewHolder(View itemView) {
super(itemView);
// we do this because we want to check when an item has been clicked:
itemView.setOnClickListener(this);
// now, like before, we assign our View variables
title = (TextView) itemView.findViewById(R.id.text1);
subtitle = (TextView) itemView.findViewById(R.id.text2);
}
@Override
public void onClick(View v) {
// The user may not set a click listener for list items, in which case our listener
// will be null, so we need to check for this
if (mOnEntryClickListener != null) {
mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
}
}
}
The only other things you need to add are a custom interface for your Adapter
and a setter method:
private OnEntryClickListener mOnEntryClickListener;
public interface OnEntryClickListener {
void onEntryClick(View view, int position);
}
public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
mOnEntryClickListener = onEntryClickListener;
}
So your new, click-supporting Adapter
is complete.
Now, let's use it...
ExampleClickAdapter clickAdapter = new ExampleClickAdapter(yourObjects);
clickAdapter.setOnEntryClickListener(new ExampleClickAdapter.OnEntryClickListener() {
@Override
public void onEntryClick(View view, int position) {
// stuff that will happen when a list item is clicked
}
});
It's basically how you would set up a normal Adapter
, except that you use your setter method that you created to control what you will do when your user clicks a particular list item.
There's a set of examples I made on this Gist on GitHub which show complete Java files you can use as a template, or to help you understand how the Adapter
works.
Solution 3
It seems you have already got an answer to your question but none of the answers here attempts to solve this problem using RxJava.
I myself is a big fan of RxJava and I never miss a chance to use it whenever possible.
Here is what I use,
public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
String[] mDataset = { "Data", "In", "Adapter" };
private final PublishSubject<String> onClickSubject = PublishSubject.create();
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final String element = mDataset[position];
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickSubject.onNext(element);
}
});
}
public Observable<String> getPositionClicks(){
return onClickSubject.asObservable();
}
}
It exposes an Observable
to intercept the click events. You now have full control of what ever you want to do with your click events (remember RxJava Operators?).
Why don't you give this a try?
Solution 4
Thanks all! I've come up with a solution (though it works for me, I'm not sure how efficient the technique is). I'm using View.OnClickListener
interface in my RecyclerView.Adapter
class. To prevent massive garbage collection, I've assigned the click listener inside onCreateViewHolder
method of my RecyclerView.Adapter
. Here is the complete implementation of my RecyclerView.Adapter
class:
package com.example.evinish.recyclerdemo;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
/**
* Created by evinish on 9/25/2015.
*/
public class MyRVAdapter extends RecyclerView.Adapter<MyViewHolder> implements View.OnClickListener{
List<Person> persons;
public MyRVAdapter(List<Person> persons) {
this.persons = persons;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout,parent,false);
MyViewHolder vh=new MyViewHolder(view);
view.setOnClickListener(this);
return vh;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.company.setText(persons.get(position).getCompany());
holder.occupation.setText(persons.get(position).getOccupation());
holder.name.setText(persons.get(position).getName());
}
@Override
public int getItemCount() {
return persons.size();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
public void clearAdapter() {
persons.clear();
notifyDataSetChanged();
}
@Override
public void onClick(View v) {
TextView clickedName=(TextView)v.findViewById(R.id.card_text1);
Toast.makeText(v.getContext(),clickedName.getText().toString(),Toast.LENGTH_LONG).show();
}
}
Please let me know if there is a better method for registering click events in RecyclerView
.
Solution 5
Late to the party, but maybe this will help others out.
The best place I have found to attach OnClickListener
to an item or child view of an item in a RecyclerView
is in the RecyclerView.Adapter
's onCreateViewHolder
. Because of RecyclerView
's recycling/reusing of item ViewHolders
, it is the most efficient place to create/attach listeners, because they are only created and attached when a new holder is created and therefore much less frequently than in onBindViewHolder
and even bind (inside the holder class).
However, there are 2-3 things needed to register changes only to specific data set item (items at specific positions in your adapter data array/list).
If you have multiple ViewHolder types, and that information helps determine what happens when you click. then you need
- The
ViewHolder
type or the access toAdapter.getViewHolderType()
.
The more obvious thing you need is the dataset (array/list) item at the position that was clicked so you can register in that item the item-specific changes (so that the changes stay with the item when the item scrolls off-screen). This means you need access to
- The dataset (usually array or list) for the
RecyclerView
and - The position of the specific item in the dataset that needs to change
You also might need to call notifyItemChanged()
or notifyItemInserted()
or (the less efficient notifyDataSetChanged()
)
So for example, in the adapter for one of my projects:
// Inflates the appropriate layout according to the ViewType.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == HOLDER_VIEW_TYPE_ONE) {
view = LayoutInflater.from(mContext).inflate(R.layout.type_one_holder_layout, parent, false);
final RecyclerView.ViewHolder holder = new TypeOneHolder(mContext,view);
//Place onclick listener after holder. holder needs to be final
//but thats fine as onCreateViewHolder usually just returns holder
//right after its created. onBindViewHolder is where changes are
//made.
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
final int position = holder.getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
if (mDataList.get(position).getTimeDisplayState() == ON_STATE) {
mMessageList.get(position).setTimeDisplayState(DEFAULT_STATE);
} else {
mMessageList.get(position).setTimeDisplayState(ON_STATE);
}
notifyItemChanged(position);
}
}
});
return holder;
}
}
Note that I used this to show and hide the last update times in the recyclerview items when the recyclerview row is clicked. The timeDisplayState is checked in my ViewHolder.bind()
and the time textview is displayed accordingly.
Note that the same result can be achieved by creating the OnCLickListener in the constructor of the ViewHolder and this is because that constructor is called in OnCreateViewHolder(). The benefit of doing this is that the adaptor can be made to do less and delegate decisions/details to the holder, thus, allowing one to make the adaptor simpler (less lines of code) and more general. The negative is that you cant just create one OnClickListener to attach to all holder types indiscriminately; you have to create listener in the constructor of every viewholder class. Not really a major issue though.
Vinit Shandilya
An electronics engineer at L. M. Ericsson. I love Java and assembly language programming. I have developed several open source EDA validation tools and data visualization programs. I'm fond of Android, though, I'm new in this arena. OS: Ubuntu 12.04 LTS/ Windows/ MAC OSX Scripting: Bash shell/ ADB Languages: Assembly (x86)/C/C++/Java Ask me about: Microprocessor and microcontroller based designs/ Open source hardware and software/Android programming/ System design and automation/ Cellular networks/ Network security
Updated on June 04, 2022Comments
-
Vinit Shandilya about 2 years
Unlike ListView, the Android RecyclerView seems way too complicated to implement. Since there is no OnItemClickListener for RecyclerView child, I've been trying to implement the following to register click events:
final RecyclerView rv=(RecyclerView)findViewById(R.id.recycler_view); LinearLayoutManager llm=new LinearLayoutManager(this); rv.setLayoutManager(llm); MyRVAdapter rva=new MyRVAdapter(persons); rv.setAdapter(rva); rv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int itemPosition = rv.indexOfChild(v); Log.d("Tag", String.valueOf(itemPosition)); } });
For some reason, I'm unable to get this code to work. The click event is not registered at all! Can anybody please tell me what am I doing wrong? I've seen some of the solutions, but I thought this is supposed to work. Thanks for your help!
-
Xaver Kapeller almost 9 yearsYou should set listeners in the constructor of the
ViewHolder
, setting them each then when the view holder is bound is not necessary. -
fractalwrench almost 9 years@XaverKapeller I have updated the answer, thanks for the tip!
-
JerabekJakub over 7 yearsI'm getting error Cannot resolve method 'asObservable()'. What am I missing?
-
Dr.jacky almost 7 years@JerabekJakub Use import rx.Observable; NOT -> io.reactivex.*;