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 to Adapter.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.