How to use Strings in RecyclerView? - java

I'm trying to use a different String, for each item in the RecyclerView, but my program always takes the first String I've put in the ArrayList, no matter which item I click on:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {
final View v = LayoutInflater.from(mContext).inflate(R.layout.cardview, null);
final ContentMain contentMain = mData.get(viewType);
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(v.getContext(), contentMain.getMyContent(),
Toast.LENGTH_LONG).show();
}
}
ViewHolder vHolder = new ViewHolder(v);
return vHolder;
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final ContentMain contentMain = mData.get(position);
holder.myContent= mData.get(position).getMyContent();
}

You shouldn't bind the onClickListener in onCreateViewHolder. Purpose of onCreateViewHolder is to construct the ViewHolder and not binding information to it.
Following code in onCreateViewHolder:
final ContentMain contentMain = mData.get(viewType);
Will always return first element because your viewType is 0. It doesn't describe the position.
Move this code in onBindViewHolder as follows:
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final ContentMain contentMain = mData.get(position);
holder.myContent= mData.get(position).getMyContent();
holder.itemView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(v.getContext(), contentMain.getMyContent(),Toast.LENGTH_LONG).show();
}
});
}

Get you data inside onClick().
You are getting data using position while creating ViewHolder. It will give wrong position since recyclerview must be calculating layout measurements -
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
final View v = LayoutInflater.from(mContext).inflate(R.layout.cardview, parent, false); //Avoid inflate layout with null parent
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final ContentMain contentMain = mData.get(getAdapterPosition());
Toast.makeText(v.getContext(), contentMain.getMyContent(), Toast.LENGTH_LONG).show();
}
}
ViewHolder vHolder = new ViewHolder(v);
return vHolder;
}
EDIT:
Probably you should define click listeners inside ViewHolder -
public static ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View v) {
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Do you stuffs here...
}
}
}
}

Related

RecyclerView onClick gives wrong item

I have a RecyclerView that shows a list of student details. The issue I face is when I click on the first item it returns the data of the second item.
I believe that it has something to do with getAdapterPosition(), but I'm not sure.
In this case, it returns the Roll No. of the second student(item) when the first student is selected.
Any help is appreciated
RecyclerViewAdapter
public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {
Context context;
public List<StudentDataModel> dataModels;
public StudentDataModel dataAdapter;
public StudentAdapter(List<StudentDataModel> getDataAdapter, Context context){
super();
this.dataModels = getDataAdapter;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.student, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(final StudentAdapter.ViewHolder viewHolder, final int position) {
dataAdapter = dataModels.get(position);
viewHolder.NameStudentCard.setText(dataAdapter.getName());
viewHolder.RankStudentCard.setText(dataAdapter.getRank());
viewHolder.RollStudentCard.setText(dataAdapter.getRoll());
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
notifyDataSetChanged();
Toast.makeText(v.getContext(), "Tapped on" + dataAdapter.getRoll(), Toast.LENGTH_SHORT).show(); //Here it returns the Roll No. of the second student(item) in the recyclerView.
Intent intent = new Intent (v.getContext(), StudentDetailsActivity.class);
v.getContext().startActivity(intent);
}
});
}
class ViewHolder extends RecyclerView.ViewHolder{
public TextView NameStudentCard;
public TextView RankStudentCard;
public TextView RollStudentCard;
public ViewHolder(View itemView) {
super(itemView);
NameStudentCard=(TextView)itemView.findViewById(R.id.textViewName);
RankStudentCard=(TextView)itemView.findViewById(R.id.textViewRank);
RollStudentCard =(TextView)itemView.findViewById(R.id.textViewEmployeeID);
}
}
#Override
public int getItemCount() {
return dataModels.size();
}
}
Yes, because you are declaring object as global object. So it is getting update with last visible row's object, because onBindViewHolder() gets called for each visible row sequentially. So it will work properly only for last visible row.
So you need to declare it inside method :
comment this :
// public StudentDataModel dataAdapter;
declare it here :
#Override
public void onBindViewHolder(final StudentAdapter.ViewHolder viewHolder, final int position) {
final StudentDataModel dataAdapter = dataModels.get(position); // add this here
}
public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {
Context context;
public List<StudentDataModel> dataModels;
public StudentAdapter(List<StudentDataModel> getDataAdapter, Context context){
super();
this.dataModels = getDataAdapter;
this.context = context;
}
public StudentDataModel dataAdapter;
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.student, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(final StudentAdapter.ViewHolder viewHolder, final int position) {
dataAdapter = dataModels.get(position);
viewHolder.NameStudentCard.setText(dataAdapter.getName());
viewHolder.RankStudentCard.setText(dataAdapter.getRank());
viewHolder.RollStudentCard.setText(dataAdapter.getRoll());
}
#Override
public int getItemCount() {
return dataModels.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public TextView NameStudentCard;
public TextView RankStudentCard;
public TextView RollStudentCard;
public ViewHolder(View itemView) {
super(itemView);
NameStudentCard=(TextView)itemView.findViewById(R.id.textViewName);
RankStudentCard=(TextView)itemView.findViewById(R.id.textViewRank);
RollStudentCard =(TextView)itemView.findViewById(R.id.textViewEmployeeID);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Tapped on" + dataModels.get(getAdapterPosition()).getRoll(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent (v.getContext(), StudentDetailsActivity.class);
v.getContext().startActivity(intent);
}
});
}
}
}
Try this code..
make interface into adapter class for click handling like this way..
onItemClickListner onItemClickListner;
public void setOnItemClickListner(RecyclerViewAdpater.onItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
public interface onItemClickListner{
void onItemClick(String data); // pass any data;
}
#Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
// below code handle click event on recycler view item.
String data=mStringList.get(position);
holder.textView.setText(data);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListner.onItemClick(data); //pass your data;
}
});
}
after that bind adapter into recyclerview and adapter not null then called below code..
recyclerViewAdpater.setOnItemClickListner(new RecyclerViewAdpater.onItemClickListner() {
#Override
public void onItemClick(String data) {
// perform your operation.
recyclerViewAdpater.notifyDataSetChanged();
}
});

Viewholder's getAdapterPosition and getLayoutPosition is always -1

I have the following AssessmentAdapter class. In the constructor of my ViewHolder class, I findViewById() to get the ImageView with the delete icon, and assign the click event to it.
I am trying to find the position of my ViewHolder, and send this to the parameterized constructor of my OnDeleteIconClicked class, but the position is always -1. Why?
Here is my code:
public class AssessmentAdapter extends RecyclerView.Adapter<AssessmentAdapter.AssessmentViewHolder> {
private List<Assessment> assessmentList;
private IAssessmentsList context;
public AssessmentAdapter(List<Assessment> assessmentList, IAssessmentsList context){
this.assessmentList = assessmentList;
this.context = context;
}
#Override
public AssessmentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rowView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.assessment_list_row, parent, false);
rowView.setOnClickListener(new OnAdapterItemClicked());
return new AssessmentViewHolder(rowView);
}
#Override
public void onBindViewHolder(AssessmentViewHolder holder, int position) {
Assessment assessment = assessmentList.get(position);
holder.txtClientName.setText(assessment.getClient());
holder.txtAssessmentDate.setText(assessment.getInspectedDate());
holder.imageView.setTag(assessment.getId());
}
#Override
public int getItemCount() {
return assessmentList.size();
}
public class AssessmentViewHolder extends RecyclerView.ViewHolder{
public TextView txtClientName, txtAssessmentDate;
public ImageView imageView;
public AssessmentViewHolder(View view){
super(view);
txtClientName = view.findViewById(R.id.txtClientName);
txtAssessmentDate = view.findViewById(R.id.txtAssessmentDate);
imageView= view.findViewById(R.id.deleteAssessment);
int layoutPosition = getLayoutPosition(); //always -1
int adapterPosition = getAdapterPosition(); //always -1
imageView.setOnClickListener(new OnDeleteIconClicked(getAdapterPosition()));
}
}
public class OnAdapterItemClicked implements View.OnClickListener{
#Override
public void onClick(View v) {
context.OnAdapterItemClicked(v);
}
}
public class OnDeleteIconClicked implements View.OnClickListener {
private int position;
public OnDeleteIconClicked(int position){
this.position = position;
}
#Override
public void onClick(View v) {
context.OnDeleteItemClicked(Integer.parseInt(v.getTag().toString()), position);
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
}
}
imageView.setOnClickListener(new OnDeleteIconClicked(getAdapterPosition()));
Take the line above and place it inside onBindViewHolder like so:
#Override
public void onBindViewHolder(AssessmentViewHolder holder, int position) {
Assessment assessment = assessmentList.get(position);
holder.txtClientName.setText(assessment.getClient());
holder.txtAssessmentDate.setText(assessment.getInspectedDate());
holder.imageView.setTag(assessment.getId());
holder.imageView.setOnClickListener(new OnDeleteIconClicked(position));
}
but the position is always -1. Why?
getAdapterPosition(); It will always return -1 when recyclerview makes layout calculations. You are calling this methods inside ViewHolder.. It means RecyclerView is doing calculations.
If you need position inside click actions of view,
inside ViewHolder { ....
...
view.setOnClickListener(new View.OnClickListener) {
#Override
public void onClick(View v) {
int position = getAdapterPosition(); // This does not return -1, because calculation is done and views have been inflated.
}
}
);
....
}
Put your click event inside onBindViewHolder
holder.imageView.setOnClickListener(new OnDeleteIconClicked(position));
public class AssessmentViewHolder extends RecyclerView.ViewHolder {
public TextView txtClientName, txtAssessmentDate;
public ImageView imageView;
public AssessmentViewHolder(View view) {
super(view);
txtClientName = view.findViewById(R.id.txtClientName);
txtAssessmentDate = view.findViewById(R.id.txtAssessmentDate);
imageView = view.findViewById(R.id.deleteAssessment);
int layoutPosition = getLayoutPosition(); //always -1
int adapterPosition = getAdapterPosition(); //always -1
// When intialise viewholder value of getAdapterPosition will be always -1
// instead of passing value from constructor, get when onClick event occure
imageView.setOnClickListener(new OnDeleteIconClicked());
} }
Change in OnDeleteIconClicked
public class OnDeleteIconClicked implements View.OnClickListener {
private int position;
#Override
public void onClick(View v) {
this.position = getAdapterPosition();
context.OnDeleteItemClicked(Integer.parseInt(v.getTag().toString()), position);
}
public int getPosition() {
return position;
}
}
getAdapterPosition(); It will always return -1 when recyclerview makes layout calculations. You are calling this methods inside ViewHolder.. It means RecyclerView is doing calculations.
If you need position inside click actions of view, call it in the public void onClick(final View v) method for example:
"#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
final Students user = mUsers.get(position);
holder.Name.setText(user.getFullname());
holder.Index.setText(user.getIndex_number());
if (user.getThumbnail().equals("default")) {
holder.profile_image.setImageResource(R.drawable.profile_pic);
} else {
Picasso.get().load(user.getThumbnail())
.placeholder(R.drawable.profile_pic)
.into(holder.profile_image);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
**list_user_id = mUsers.get(position).getId();**
Intent Sub = new Intent(mContext, UserProfileActivity.class);
Sub.putExtra("user_id1", list_user_id);
mContext.startActivity(Sub);
BUT NOT
getAdapterPosition(); It will always return -1 when recyclerview makes layout calculations. You are calling this methods inside ViewHolder.. It means RecyclerView is doing calculations.
If you need position inside click actions of view, call it in the public void onClick(final View v) method for example:
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
final Students user = mUsers.get(position);
holder.Name.setText(user.getFullname());
holder.Index.setText(user.getIndex_number());
**list_user_id = mUsers.get(position).getId();**
if (user.getThumbnail().equals("default")) {
holder.profile_image.setImageResource(R.drawable.profile_pic);
} else {
Picasso.get().load(user.getThumbnail())
.placeholder(R.drawable.profile_pic)
.into(holder.profile_image);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
Intent Sub = new Intent(mContext, UserProfileActivity.class);
Sub.putExtra("user_id1", list_user_id);
mContext.startActivity(Sub);

RecyclerView onItemClickListener

I'm looking to build a program where someone will click from a list of text and this will take them to another activity.
I'm aware that RecyclerView doesn't have a listener for clicks, like ListView but, after several attempts, I am unable to get this to work.
Below is the code I have attempted to implement:
MyChatRecyclerViewAdapter.java
public class MyChatRecyclerViewAdapter extends RecyclerView.Adapter<MyChatRecyclerViewAdapter.ViewHolder> {
private final List<Chat> mValues;
private final OnItemClickListener listener;
//private final OnListFragmentInteractionListener mListener;
public MyChatRecyclerViewAdapter(List<Chat> items, OnItemClickListener listener) {
this.mValues = items;
//mListener = listener;
this.listener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_chat, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mIdView.setText(mValues.get(position).id);
holder.bind(mValues.get(position), listener);
holder.mContentView.setText(mValues.get(position).content);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != listener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
listener.onListFragmentInteraction(holder.mItem);
}
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
public interface OnItemClickListener {
//public void onItemClick(View view , int position);
void onItemClick(Chat item);
}
public class ViewHolder extends RecyclerView.ViewHolder{
public final View mView;
public final TextView mIdView;
public final TextView mContentView;
public Chat mItem;
OnItemClickListener mItemClickListener;
/*#Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getPosition());
}
}*/
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
mIdView = (TextView) itemView.findViewById(R.id.id);
mContentView = (TextView) itemView.findViewById(R.id.content);
}
public void bind(final Chat item, final OnItemClickListener listener){
itemView.setOnClickListener(new View.OnClickListener(){
#override
public void onClick(View v){
listener.onItemClick(item);
}
});
}
#Override
public String toString() {
return super.toString() + " '" + mContentView.getText() + "'";
}
}
}
This is the tutorial I have attempted to follow: https://antonioleiva.com/recyclerview-listener/
Inside a RecyclerView, views are recycled to improve performance. You shouln't place the OnClickListener inside the onBindViewHolder as the position will not always be correct.
Try moving the OnClickListener inside the constructor of the ViewHolder and getting the current position by calling getAdapterPosition() :
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
mIdView = (TextView) itemView.findViewById(R.id.id);
mContentView = (TextView) itemView.findViewById(R.id.content);
mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final int position = getAdapterPosition();
if (null != listener) {
listener.onListFragmentInteraction(mValues.get(position));
}
}
});
}
add following method to your MyChatRecyclerViewAdapter :
public void setClickListener(OnItemClickListener listener) {
this.listener = listener;
}
and call
mAdapter.setClickListener(new MyChatRecyclerViewAdapter.OnItemClickListener() {
#Override
public void onItemClick(Chat chat) {
}
});
from your Activity/Fragment class
You are setting onclick listener 2 times. 1 in onBind() and 1 in View holder.
Remove from on Bind() and in view holder use listener object to pass event to specific listener.
Layout.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
if (getListener() != null) {
getListener().onLayoutClick(view, this.getAdapterPosition());
}
}
});
public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
//inflate the view
View view =LayoutInflator.from(parent.getContext()).inflate(R.layout.layoutID,null);
ViewHolder holder = new ViewHolder(view);
//here we can set onClicklistener
view.setOnClickListener(new View.OnClickListeener(){
public void onClick(View v)
{
//Your action stuff
}
});
return holder;
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();
}
}

Android: invert RecyclerView positions

I'm setting a RecyclerView behaving like a list, I want a button in the bottom of the list that when clicked adds more views, I'm thinking the easier way to do it is to make the position 0 as the first one in the bottom, and increasing the position to the top, so I can add views when the the view in position 0 is clicked.
If there is a better aproach for this problem do share.
Here is my adapter:
public class AddEventsAdapter extends RecyclerView.Adapter<AddEventsAdapter.ViewHolder> {
public List<String> items = new ArrayList<>();
public void addItem(String name) {
notifyItemInserted(items.size() - 1);
items.add(name);
}
public void removeItem(int position) {
items.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, items.size());
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.add_event_item, parent, false);
return new ViewHolder(view);
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setData(position);
holder.eventName.setText(i + "");
if(position == 0)
{
holder.theLayout.setBackgroundColor(Color.parseColor("#7F9099"));
holder.eventName.setText("Add");
}
}
static int i;
class ViewHolder extends RecyclerView.ViewHolder{
public TextView eventName;
public RelativeLayout theLayout;
public ViewHolder(final View itemView) {
super(itemView);
eventName = (TextView)itemView.findViewById(R.id.eventName);
theLayout = (RelativeLayout)itemView.findViewById(R.id.backgroundevent);
theLayout.setId(++i);
}
public void setData(final int position) {
theLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (position == items.size() - 1) {
addItem("");
} else {
removeItem(position);
}
}
});
}
}
}
You may notice some errors in that, I've been over it for the last 10 hours and I'm having a logic breakdown
It's solved by addind this line to the LayoutManager .setReverseLayout(true);
you can add a footer view at the end of the list and inside that you can add your button. This is the link to create a footer in recycler view https://github.com/u3breeze/android-RecyclerView-WithHeaderAndFooter. You can add the views in the normal way

Why doesn't RecyclerView have onItemClickListener()?

I was exploring RecyclerView and I was surprised to see that RecyclerView does not have onItemClickListener().
I've two question.
Main Question
I want to know why Google removed onItemClickListener()?
Is there a performance issue or something else?
Secondary Question
I solved my problem by writing onClick in my RecyclerView.Adapter:
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
}
#Override
public void onClick(View v) {
}
}
Is this ok / is there any better way?
tl;dr 2016 Use RxJava and a PublishSubject to expose an Observable for the clicks.
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();
}
}
Original Post:
Since the introduction of ListView, onItemClickListener has been problematic. The moment you have a click listener for any of the internal elements the callback would not be triggered but it wasn't notified or well documented (if at all) so there was a lot of confusion and SO questions about it.
Given that RecyclerView takes it a step further and doesn't have a concept of a row/column, but rather an arbitrarily laid out amount of children, they have delegated the onClick to each one of them, or to programmer implementation.
Think of Recyclerview not as a ListView 1:1 replacement but rather as a more flexible component for complex use cases. And as you say, your solution is what google expected of you. Now you have an adapter who can delegate onClick to an interface passed on the constructor, which is the correct pattern for both ListView and Recyclerview.
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public IMyViewHolderClicks mListener;
public ViewHolder(View itemLayoutView, IMyViewHolderClicks listener) {
super(itemLayoutView);
mListener = listener;
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
imgViewIcon.setOnClickListener(this);
itemLayoutView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v instanceof ImageView){
mListener.onTomato((ImageView)v);
} else {
mListener.onPotato(v);
}
}
public static interface IMyViewHolderClicks {
public void onPotato(View caller);
public void onTomato(ImageView callerImage);
}
}
and then on your adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
String[] mDataset = { "Data" };
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_layout, parent, false);
MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() {
public void onPotato(View caller) { Log.d("VEGETABLES", "Poh-tah-tos"); };
public void onTomato(ImageView callerImage) { Log.d("VEGETABLES", "To-m8-tohs"); }
});
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Get element from your dataset at this position
// Replace the contents of the view with that element
// Clear the ones that won't be used
holder.txtViewTitle.setText(mDataset[position]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
...
Now look into that last piece of code: onCreateViewHolder(ViewGroup parent, int viewType) the signature already suggest different view types. For each one of them you'll require a different viewholder too, and subsequently each one of them can have a different set of clicks. Or you can just create a generic viewholder that takes any view and one onClickListener and applies accordingly. Or delegate up one level to the orchestrator so several fragments/activities have the same list with different click behaviour. Again, all flexibility is on your side.
It is a really needed component and fairly close to what our internal implementations and improvements to ListView were until now. It's good that Google finally acknowledges it.
Why the RecyclerView has no onItemClickListener
The RecyclerView is a toolbox, in contrast of the old ListView it has less build in features and more flexibility. The onItemClickListener is not the only feature being removed from ListView. But it has lot of listeners and method to extend it to your liking, it's far more powerful in the right hands ;).
In my opinion the most complex feature removed in RecyclerView is the Fast Scroll. Most of the other features can be easily re-implemented.
If you want to know what other cool features RecyclerView added read this answer to another question.
Memory efficient - drop-in solution for onItemClickListener
This solution has been proposed by Hugo Visser, an Android GDE, right after RecyclerView was released. He made a licence-free class available for you to just drop in your code and use it.
It showcase some of the versatility introduced with RecyclerView by making use of RecyclerView.OnChildAttachStateChangeListener.
Edit 2019: kotlin version by me, java one, from Hugo Visser, kept below
Kotlin / Java
Create a file values/ids.xml and put this in it:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
</resources>
then add the code below to your source
Kotlin
Usage:
recyclerView.onItemClick { recyclerView, position, v ->
// do it
}
(it also support long item click and see below for another feature I've added).
implementation (my adaptation to Hugo Visser Java code):
typealias OnRecyclerViewItemClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Unit
typealias OnRecyclerViewItemLongClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Boolean
class ItemClickSupport private constructor(private val recyclerView: RecyclerView) {
private var onItemClickListener: OnRecyclerViewItemClickListener? = null
private var onItemLongClickListener: OnRecyclerViewItemLongClickListener? = null
private val attachListener: RecyclerView.OnChildAttachStateChangeListener = object : RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
// every time a new child view is attached add click listeners to it
val holder = this#ItemClickSupport.recyclerView.getChildViewHolder(view)
.takeIf { it is ItemClickSupportViewHolder } as? ItemClickSupportViewHolder
if (onItemClickListener != null && holder?.isClickable != false) {
view.setOnClickListener(onClickListener)
}
if (onItemLongClickListener != null && holder?.isLongClickable != false) {
view.setOnLongClickListener(onLongClickListener)
}
}
override fun onChildViewDetachedFromWindow(view: View) {
}
}
init {
// the ID must be declared in XML, used to avoid
// replacing the ItemClickSupport without removing
// the old one from the RecyclerView
this.recyclerView.setTag(R.id.item_click_support, this)
this.recyclerView.addOnChildAttachStateChangeListener(attachListener)
}
companion object {
fun addTo(view: RecyclerView): ItemClickSupport {
// if there's already an ItemClickSupport attached
// to this RecyclerView do not replace it, use it
var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as? ItemClickSupport
if (support == null) {
support = ItemClickSupport(view)
}
return support
}
fun removeFrom(view: RecyclerView): ItemClickSupport? {
val support = view.getTag(R.id.item_click_support) as? ItemClickSupport
support?.detach(view)
return support
}
}
private val onClickListener = View.OnClickListener { v ->
val listener = onItemClickListener ?: return#OnClickListener
// ask the RecyclerView for the viewHolder of this view.
// then use it to get the position for the adapter
val holder = this.recyclerView.getChildViewHolder(v)
listener.invoke(this.recyclerView, holder.adapterPosition, v)
}
private val onLongClickListener = View.OnLongClickListener { v ->
val listener = onItemLongClickListener ?: return#OnLongClickListener false
val holder = this.recyclerView.getChildViewHolder(v)
return#OnLongClickListener listener.invoke(this.recyclerView, holder.adapterPosition, v)
}
private fun detach(view: RecyclerView) {
view.removeOnChildAttachStateChangeListener(attachListener)
view.setTag(R.id.item_click_support, null)
}
fun onItemClick(listener: OnRecyclerViewItemClickListener?): ItemClickSupport {
onItemClickListener = listener
return this
}
fun onItemLongClick(listener: OnRecyclerViewItemLongClickListener?): ItemClickSupport {
onItemLongClickListener = listener
return this
}
}
/** Give click-ability and long-click-ability control to the ViewHolder */
interface ItemClickSupportViewHolder {
val isClickable: Boolean get() = true
val isLongClickable: Boolean get() = true
}
// Extension function
fun RecyclerView.addItemClickSupport(configuration: ItemClickSupport.() -> Unit = {}) = ItemClickSupport.addTo(this).apply(configuration)
fun RecyclerView.removeItemClickSupport() = ItemClickSupport.removeFrom(this)
fun RecyclerView.onItemClick(onClick: OnRecyclerViewItemClickListener) {
addItemClickSupport { onItemClick(onClick) }
}
fun RecyclerView.onItemLongClick(onLongClick: OnRecyclerViewItemLongClickListener) {
addItemClickSupport { onItemLongClick(onLongClick) }
}
(Remember you also need to add an XML file, see above this section)
Bonus feature of Kotlin version
Sometimes you do not want all the items of the RecyclerView to be clickable.
To handle this I've introduced the ItemClickSupportViewHolder interface that you can use on your ViewHolder to control which item is clickable.
Example:
class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
override val isClickable: Boolean get() = false
override val isLongClickable: Boolean get() = false
}
Java
Usage:
ItemClickSupport.addTo(mRecyclerView)
.setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
(it also support long item click)
Implementation (comments added by me):
public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
// ask the RecyclerView for the viewHolder of this view.
// then use it to get the position for the adapter
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
}
};
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
return false;
}
};
private RecyclerView.OnChildAttachStateChangeListener mAttachListener
= new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(View view) {
// every time a new child view is attached add click listeners to it
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}
#Override
public void onChildViewDetachedFromWindow(View view) {
}
};
private ItemClickSupport(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
// the ID must be declared in XML, used to avoid
// replacing the ItemClickSupport without removing
// the old one from the RecyclerView
mRecyclerView.setTag(R.id.item_click_support, this);
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}
public static ItemClickSupport addTo(RecyclerView view) {
// if there's already an ItemClickSupport attached
// to this RecyclerView do not replace it, use it
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support == null) {
support = new ItemClickSupport(view);
}
return support;
}
public static ItemClickSupport removeFrom(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support != null) {
support.detach(view);
}
return support;
}
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
return this;
}
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
return this;
}
private void detach(RecyclerView view) {
view.removeOnChildAttachStateChangeListener(mAttachListener);
view.setTag(R.id.item_click_support, null);
}
public interface OnItemClickListener {
void onItemClicked(RecyclerView recyclerView, int position, View v);
}
public interface OnItemLongClickListener {
boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}
}
How it works (why it's efficient)
This class works by attaching a RecyclerView.OnChildAttachStateChangeListener to the RecyclerView. This listener is notified every time a child is attached or detached from the RecyclerView. The code use this to append a tap/long click listener to the view. That listener ask the RecyclerView for the RecyclerView.ViewHolder which contains the position.
This is more efficient then other solutions because it avoid creating multiple listeners for each view and keep destroying and creating them while the RecyclerView is being scrolled.
You could also adapt the code to give you back the holder itself if you need more.
Final remark
Keep in mind that it's COMPLETELY fine to handle it in your adapter by setting on each view of your list a click listener, like other answer proposed.
It's just not the most efficient thing to do (you create a new listener every time you reuse a view) but it works and in most cases it's not an issue.
It is also a bit against separation of concerns cause it's not really the Job of the Adapter to delegate click events.
I like this way and I'm using it
Inside
public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
Put
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());
And create this class anywhere you want it
class MyOnClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
int itemPosition = recyclerView.indexOfChild(v);
Log.e("Clicked and Position is ",String.valueOf(itemPosition));
}
}
I've read before that there is a better way but I like this way is easy and not complicated.
Android Recyclerview With onItemClickListener,
Why we cant try this is working like ListView only.
Source : Link
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
public interface OnItemClickListener {
public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
And Set this to RecyclerView:
recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
// TODO Handle item click
Log.e("#####",""+position);
}
})
);
Thanks to #marmor, I updated my answer.
I think it's a good solution to handle the onClick() in the ViewHolder class constructor and pass it to the parent class via OnItemClickListener interface.
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private LayoutInflater layoutInflater;
private List<MyObject> items;
private AdapterView.OnItemClickListener onItemClickListener;
public MyAdapter(Context context, AdapterView.OnItemClickListener onItemClickListener, List<MyObject> items) {
layoutInflater = LayoutInflater.from(context);
this.items = items;
this.onItemClickListener = onItemClickListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.my_row_layout, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
MyObject item = items.get(position);
}
public MyObject getItem(int position) {
return items.get(position);
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView title;
private ImageView avatar;
public ViewHolder(View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title);
avatar = itemView.findViewById(R.id.avatar);
title.setOnClickListener(this);
avatar.setOnClickListener(this);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
//passing the clicked position to the parent class
onItemClickListener.onItemClick(null, view, getAdapterPosition(), view.getId());
}
}
}
Usage of adapter in other classes:
MyFragment.java
public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {
private RecyclerView recycleview;
private MyAdapter adapter;
.
.
.
private void init(Context context) {
//passing this fragment as OnItemClickListener to the adapter
adapter = new MyAdapter(context, this, items);
recycleview.setAdapter(adapter);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//you can get the clicked item from the adapter using its position
MyObject item = adapter.getItem(position);
//you can also find out which view was clicked
switch (view.getId()) {
case R.id.title:
//title view was clicked
break;
case R.id.avatar:
//avatar view was clicked
break;
default:
//the whole row was clicked
}
}
}
> How RecyclerView is different from Listview?
One difference is that there is LayoutManager class with RecyclerView by which you can manage your RecyclerView like-
Horizontal or Vertical scrolling by LinearLayoutManager
GridLayout by GridLayoutManager
Staggered GridLayout by StaggeredGridLayoutManager
Like for horizontal scrolling for RecyclerView-
LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(llm);
Guys use this code in Your main activity. Very Efficient Method
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.users_list);
UsersAdapter adapter = new UsersAdapter(users, this);
recyclerView.setAdapter(adapter);
adapter.setOnCardClickListner(this);
Here is your Adapter class.
public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UserViewHolder> {
private ArrayList<User> mDataSet;
OnCardClickListner onCardClickListner;
public UsersAdapter(ArrayList<User> mDataSet) {
this.mDataSet = mDataSet;
}
#Override
public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_row_layout, parent, false);
UserViewHolder userViewHolder = new UserViewHolder(v);
return userViewHolder;
}
#Override
public void onBindViewHolder(UserViewHolder holder, final int position) {
holder.name_entry.setText(mDataSet.get(position).getUser_name());
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onCardClickListner.OnCardClicked(v, position);
}
});
}
#Override
public int getItemCount() {
return mDataSet.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
public static class UserViewHolder extends RecyclerView.ViewHolder {
CardView cardView;
TextView name_entry;
public UserViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView.findViewById(R.id.user_layout);
name_entry = (TextView) itemView.findViewById(R.id.name_entry);
}
}
public interface OnCardClickListner {
void OnCardClicked(View view, int position);
}
public void setOnCardClickListner(OnCardClickListner onCardClickListner) {
this.onCardClickListner = onCardClickListner;
}
}
After this you will get this override method in your activity.
#Override
public void OnCardClicked(View view, int position) {
Log.d("OnClick", "Card Position" + position);
}
How to put it all together example...
onClick() handling
Cursor - RecyclerView
ViewHolder types
public class OrderListCursorAdapter extends CursorRecyclerViewAdapter<OrderListCursorAdapter.ViewHolder> {
private static final String TAG = OrderListCursorAdapter.class.getSimpleName();
private static final int ID_VIEW_HOLDER_ACTUAL = 0;
private static final int ID_VIEW_HOLDER = 1;
public OrderListCursorAdapter(Context context, Cursor cursor) {
super(context, cursor);
}
public static class ViewHolderActual extends ViewHolder {
private static final String TAG = ViewHolderActual.class.getSimpleName();
protected IViewHolderClick listener;
protected Button button;
public ViewHolderActual(View v, IViewHolderClick listener) {
super(v, listener);
this.listener = listener;
button = (Button) v.findViewById(R.id.orderList_item_button);
button.setOnClickListener(this);
}
public void initFromData(OrderData data) {
Log.d(TAG, "><initFromData(data=" + data + ")");
orderId = data.getId();
vAddressStart.setText(data.getAddressStart());
vAddressEnd.setText(data.getAddressEnd());
}
#Override
public void onClick(View view) {
if (view instanceof Button) {
listener.onButtonClick((Button) view, getPosition(), this);
} else {
super.onClick(view);
}
}
public interface IViewHolderClick extends ViewHolder.IViewHolderClick {
public void onButtonClick(Button button, int position, ViewHolder viewHolder);
}
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private static final String TAG = ViewHolder.class.getSimpleName();
protected long orderId;
protected IViewHolderClick listener;
protected TextView vAddressStart;
protected TextView vAddressEnd;
protected TextView vStatus;
public ViewHolder(View v, IViewHolderClick listener) {
super(v);
this.listener = listener;
v.setOnClickListener(this);
vAddressStart = (TextView) v.findViewById(R.id.addressStart);
vAddressEnd = (TextView) v.findViewById(R.id.addressEnd);
vStatus = (TextView) v.findViewById(R.id.status);
}
public void initFromData(OrderData data) {
Log.d(TAG, "><initFromData(data=" + data + ")");
orderId = data.getId();
vAddressStart.setText(data.getAddressStart());
vAddressEnd.setText(data.getAddressEnd());
}
public long getOrderId() {
return orderId;
}
#Override
public void onClick(View view) {
listener.onCardClick(view, getPosition(), this);
}
public interface IViewHolderClick {
public void onCardClick(View view, int position, ViewHolder viewHolder);
}
}
#Override
public int getItemViewType(int position) {
return position == 0 ? ID_VIEW_HOLDER_ACTUAL : ID_VIEW_HOLDER;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(TAG, ">>onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")");
ViewHolder result;
switch (viewType) {
case ID_VIEW_HOLDER_ACTUAL: {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout_actual, parent, false);
result = new ViewHolderActual(itemView, new ViewHolderActual.IViewHolderClick() {
#Override
public void onCardClick(View view, int position, ViewHolder viewHolder) {
Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
view.getContext().startActivity(intent);
}
#Override
public void onButtonClick(Button button, int position, ViewHolder viewHolder) {
Log.d(TAG, "><onButtonClick(button=" + button + ", position=" + position + ", viewHolder=" + viewHolder + ")");
Intent intent = new Intent(button.getContext(), OrderMapActivity.class);
intent.putExtra(OrderMapActivity.ARG_ORDER_ID, viewHolder.getOrderId());
button.getContext().startActivity(intent);
}
});
break;
}
case ID_VIEW_HOLDER:
default: {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout, parent, false);
result = new ViewHolder(itemView, new ViewHolder.IViewHolderClick() {
#Override
public void onCardClick(View view, int position, ViewHolder viewHolder) {
Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
view.getContext().startActivity(intent);
}
});
break;
}
}
Log.d(TAG, "<<onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")= " + result);
return result;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
Log.d(TAG, "><onBindViewHolder(viewHolder=" + viewHolder + ", cursor=" + cursor + ")");
final OrderData orderData = new OrderData(cursor);
viewHolder.initFromData(orderData);
}
}
Following up MLProgrammer-CiM's excellent RxJava solution
Consume / Observe clicks
Consumer<String> mClickConsumer = new Consumer<String>() {
#Override
public void accept(#NonNull String element) throws Exception {
Toast.makeText(getApplicationContext(), element +" was clicked", Toast.LENGTH_LONG).show();
}
};
ReactiveAdapter rxAdapter = new ReactiveAdapter();
rxAdapter.getPositionClicks().subscribe(mClickConsumer);
RxJava 2.+
Modify the original tl;dr as:
public Observable<String> getPositionClicks(){
return onClickSubject;
}
PublishSubject#asObservable() was removed. Just return the PublishSubject which is an Observable.
RecyclerView doesn't have an onItemClickListener because RecyclerView is responsible for recycling views (surprise!), so it's the responsibility of the view that is recycled to handle the click events it receives.
This actually makes it much easier to use, especially if you had items that can be clicked in multiple places.
Anyways, detecting click on a RecyclerView item is very easy. All you need to do is define an interface (if you're not using Kotlin, in which case you just pass in a lambda):
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
private final Clicks clicks;
public MyAdapter(Clicks clicks) {
this.clicks = clicks;
}
private List<MyObject> items = Collections.emptyList();
public void updateData(List<MyObject> items) {
this.items = items;
notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations
}
public interface Clicks {
void onItemSelected(MyObject myObject, int position);
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private MyObject myObject;
public MyViewHolder(View view) {
super(view);
// bind views
view.setOnClickListener((v) -> {
int adapterPosition = getBindingAdapterPosition();
if(adapterPosition >= 0) {
clicks.onItemSelected(myObject, adapterPosition);
}
});
}
public void bind(MyObject myObject) {
this.myObject = myObject;
// bind data to views
}
}
}
Same code in Kotlin:
class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() {
private var items: List<MyObject> = Collections.emptyList()
fun updateData(items: List<MyObject>) {
this.items = items
notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations
}
inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) {
private lateinit var myObject: MyObject
init {
// binds views
myView.onClick {
val adapterPosition = getBindingAdapterPosition()
if(adapterPosition >= 0) {
itemClicks.invoke(myObject, adapterPosition)
}
}
}
fun bind(myObject: MyObject) {
this.myObject = myObject
// bind data to views
}
}
}
Thing you DON'T need to do:
1.) you don't need to intercept touch events manually
2.) you don't need to mess around with child attach state change listeners
3.) you don't need PublishSubject/PublishRelay from RxJava
Just use a click listener.
As far as I understand MLProgrammer-CiM answer, simply it's possible to just do this:
class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private ImageView image;
private TextView title;
private TextView price;
public MyViewHolder(View itemView) {
super(itemView);
image = (ImageView)itemView.findViewById(R.id.horizontal_list_image);
title = (TextView)itemView.findViewById(R.id.horizontal_list_title);
price = (TextView)itemView.findViewById(R.id.horizontal_list_price);
image.setOnClickListener(this);
title.setOnClickListener(this);
price.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(context, "Item click nr: "+getLayoutPosition(), Toast.LENGTH_SHORT).show();
}
}
After reading #MLProgrammer-CiM's answer, here is my code:
class NormalViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
#Bind(R.id.card_item_normal)
CardView cardView;
public NormalViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
cardView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v instanceof CardView) {
// use getAdapterPosition() instead of getLayoutPosition()
int itemPosition = getAdapterPosition();
removeItem(itemPosition);
}
}
}
I have done this way, its very simple:
Just add 1 Line for Clicked RecyclerView position:
int position = getLayoutPosition()
Full code for ViewHolder class:
private class ChildViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView txtView;
public ChildViewHolder(View itemView) {
super(itemView);
imageView= (ImageView)itemView.findViewById(R.id.imageView);
txtView= (TextView) itemView.findViewById(R.id.txtView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.i("RecyclerView Item Click Position", String.valueOf(getLayoutPosition()));
}
});
}
}
Hope this will help you.
I use this method to start an Intent from RecyclerView:
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
final MyClass myClass = mList.get(i);
viewHolder.txtViewTitle.setText(myclass.name);
...
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
Intent detailIntent = new Intent(mContext, type.class);
detailIntent.putExtra("MyClass", myclass);
mContext.startActivity(detailIntent);
}
}
);
See my approach on this:
First declare an interface like this:
/**
* Interface used for delegating item click events in a {#link android.support.v7.widget.RecyclerView}
* Created by Alex on 11/28/2015.
*/
public interface OnRecyclerItemClickListener<T> {
/**
* Called when a click occurred inside a recyclerView item view
* #param view that was clicked
* #param position of the clicked view
* #param item the concrete data that is displayed through the clicked view
*/
void onItemClick(View view, int position, T item);
}
Then create the adapter:
public class CustomRecyclerAdapter extends RecyclerView.Adapter {
private class InternalClickListener implements View.OnClickListener{
#Override
public void onClick(View v) {
if(mRecyclerView != null && mItemClickListener != null){
// find the position of the item that was clicked
int position = mRecyclerView.getChildAdapterPosition(v);
Data data = getItem(position);
// notify the main listener
mItemClickListener.onItemClick(v, position, data);
}
}
}
private final OnRecyclerItemClickListener mItemClickListener;
private RecyclerView mRecyclerView;
private InternalClickListener mInternalClickListener;
/**
*
* #param itemClickListener used to trigger an item click event
*/
public PlayerListRecyclerAdapter(OnRecyclerItemClickListener itemClickListener){
mItemClickListener = itemClickListener;
mInternalClickListener = new InternalClickListener();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);
v.setOnClickListener(mInternalClickListener);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// do your binding here
}
#Override
public int getItemCount() {
return mDataSet.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
}
#Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
mRecyclerView = null;
}
public Data getItem(int position){
return mDataset.get(position);
}
}
And now let's see how to integrate this from a fragment:
public class TestFragment extends Fragment implements OnRecyclerItemClickListener<Data>{
private RecyclerView mRecyclerView;
#Override
public void onItemClick(View view, int position, Data item) {
// do something
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.test_fragment, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
mRecyclerView = view.findViewById(idOfTheRecycler);
mRecyclerView .setAdapter(new CustomRecyclerAdapter(this));
}
If you want to add onClick() to the child view of items, for example, a button in item, I found that you can do it easily in onCreateViewHolder() of your own RecyclerView.Adapter just like this:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.cell, null);
Button btn = (Button) v.findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do it
}
});
return new MyViewHolder(v);
}
i don't know whether it's a good way, but it works well. If anyone has a better idea, very glad to tell me and correct my answer! :)
Here is a way to implement it quite easily if you have a list of POJOs and want to retrieve one on click from outside the adapter.
In your adapter, create a listener for the click events and a method to set it:
public class MyAdapter extends RecyclerView.Adapter<SitesListAdapter.ViewHolder> {
...
private List<MyPojo> mMyPojos;
private static OnItemClickListener mOnItemClickListener;
...
public interface OnItemClickListener {
public void onItemClick(MyPojo pojo);
}
...
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
mOnItemClickListener = onItemClickListener;
}
...
}
In your ViewHolder, implement onClickListener and create a class member to temporarily store the POJO the view is presenting, that way (this is an example, creating a setter would be better):
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public MyPojo mCurrentPojo;
...
public ViewHolder(View view) {
super(v);
...
view.setOnClickListener(this); //You could set this on part of the layout too
}
...
#Override
public void onClick(View view) {
if(mOnItemClickListener != null && mCurrentPojo != null){
mOnItemClickListener.onItemClick(mCurrentPojo);
}
}
Back in your adapter, set the current POJO when the ViewHolder is bound (or to null if the current view doesn't have one):
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final MyPojo currentPojo = mMyPojos.get(position);
holder.mCurrentPojo = currentPojo;
...
That's it, now you can use it like this from your fragment/activity:
mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
#Override
public void onItemClick(MyPojo pojo) {
//Do whatever you want with your pojo here
}
});
Yes you can
public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
//inflate the view
View view = LayoutInflator.from(parent.getContext()).inflate(R.layout.layoutID,null);
ViewHolder holder = new ViewHolder(view);
//here we can set onClicklistener
view.setOnClickListener(new View.OnClickListeener(){
public void onClick(View v)
{
//action
}
});
return holder;
This worked for me:
#Override
public void onBindViewHolder(PlacesListViewAdapter.ViewHolder holder, int position) {
----
----
----
// Set setOnClickListener(holder);
}
#Override
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
----
----
----
#Override
public void onClick(View view) {
// Use to get the item clicked getAdapterPosition()
}
}
Here you can handle multiple onclick see below code and it is very efficient
public class RVNewsAdapter extends RecyclerView.Adapter<RVNewsAdapter.FeedHolder> {
private Context context;
List<News> newsList;
// Allows to remember the last item shown on screen
private int lastPosition = -1;
public RVNewsAdapter(List<News> newsList, Context context) {
this.newsList = newsList;
this.context = context;
}
public static class FeedHolder extends RecyclerView.ViewHolder implements OnClickListener {
ImageView img_main;
TextView tv_title;
Button bt_facebook, bt_twitter, bt_share, bt_comment;
public FeedHolder(View itemView) {
super(itemView);
img_main = (ImageView) itemView.findViewById(R.id.img_main);
tv_title = (TextView) itemView.findViewById(R.id.tv_title);
bt_facebook = (Button) itemView.findViewById(R.id.bt_facebook);
bt_twitter = (Button) itemView.findViewById(R.id.bt_twitter);
bt_share = (Button) itemView.findViewById(R.id.bt_share);
bt_comment = (Button) itemView.findViewById(R.id.bt_comment);
img_main.setOnClickListener(this);
bt_facebook.setOnClickListener(this);
bt_twitter.setOnClickListener(this);
bt_comment.setOnClickListener(this);
bt_share.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v.getId() == bt_comment.getId()) {
Toast.makeText(v.getContext(), "Comment " , Toast.LENGTH_SHORT).show();
} else if (v.getId() == bt_facebook.getId()) {
Toast.makeText(v.getContext(), "Facebook " , Toast.LENGTH_SHORT).show();
} else if (v.getId() == bt_twitter.getId()) {
Toast.makeText(v.getContext(), "Twitter " , Toast.LENGTH_SHORT).show();
} else if (v.getId() == bt_share.getId()) {
Toast.makeText(v.getContext(), "share " , Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public FeedHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.feed_row, parent, false);
FeedHolder feedHolder = new FeedHolder(view);
return feedHolder;
}
#Override
public void onBindViewHolder(FeedHolder holder, int position) {
holder.tv_title.setText(newsList.get(position).getTitle());
// Here you apply the animation when the view is bound
setAnimation(holder.img_main, position);
}
#Override
public int getItemCount() {
return newsList.size();
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
}
Modified my comment...
public class MyViewHolder extends RecyclerView.ViewHolder {
private Context mContext;
public MyViewHolder(View itemView) {
super(itemView);
mContext = itemView.getContext();
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int itemPosition = getLayoutPosition();
Toast.makeText(mContext, "" + itemPosition, Toast.LENGTH_SHORT).show();
}
});
}
Check this one in which I have implemented all the things with a proper way
RecyclerViewHolder Class
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
//view holder is for girdview as we used in the listView
public ImageView imageView,imageView2;
public RecyclerViewHolder(View itemView) {
super(itemView);
this.imageView=(ImageView)itemView.findViewById(R.id.image);
}
}
Adapter
public class RecyclerView_Adapter extends RecyclerView.Adapter<RecyclerViewHolder> {
//RecyclerView will extend to recayclerview Adapter
private ArrayList<ModelClass> arrayList;
private Context context;
private static RecyclerViewClickListener itemListener;
//constructor of the RecyclerView Adapter
RecyclerView_Adapter(Context context,ArrayList<ModelClass> arrayList,RecyclerViewClickListener itemListener){
this.context=context;
this.arrayList=arrayList;
this.itemListener=itemListener;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//this method will inflate the custom layout and return as viewHolder
LayoutInflater layoutInflater=LayoutInflater.from(parent.getContext());
ViewGroup mainGroup=(ViewGroup) layoutInflater.inflate(R.layout.single_item,parent,false);
RecyclerViewHolder listHolder=new RecyclerViewHolder(mainGroup);
return listHolder;
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, final int position) {
final ModelClass modelClass=arrayList.get(position);
//holder
RecyclerViewHolder mainHolder=(RecyclerViewHolder)holder;
//convert the drawable image into bitmap
Bitmap image= BitmapFactory.decodeResource(context.getResources(),modelClass.getImage());
//set the image into imageView
mainHolder.imageView.setImageBitmap(image);
//to handle on click event when clicked on the recyclerview item and
// get it through the RecyclerViewHolder class we have defined the views there
mainHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//get the position of the image which is clicked
itemListener.recyclerViewListClicked(v,position);
}
});
}
#Override
public int getItemCount() {
return (null!=arrayList?arrayList.size():0);
}
}
The interface
public interface RecyclerViewClickListener {
//this is method to handle the event when clicked on the image in Recyclerview
public void recyclerViewListClicked(View v,int position);
}
//and to call this method in activity
RecyclerView_Adapter adapter=new RecyclerView_Adapter(Wallpaper.this,arrayList,this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
#Override
public void recyclerViewListClicked(View v,int position){
imageView.setImageResource(wallpaperImages[position]);
}
Access the mainView of rowLayout(cell) for you RecyclerView and in your OnBindViewHolder write this code:
#Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
Movie movie = moviesList.get(position);
holder.mainView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
System.out.println("pos " + position);
}
});
}
it worked for me. Hope it will help. Most simplest way.
Inside View Holder
class GeneralViewHolder extends RecyclerView.ViewHolder {
View cachedView = null;
public GeneralViewHolder(View itemView) {
super(itemView);
cachedView = itemView;
}
Inside OnBindViewHolder()
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final GeneralViewHolder generalViewHolder = (GeneralViewHolder) holder;
generalViewHolder.cachedView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "item Clicked at "+position, Toast.LENGTH_SHORT).show();
}
});
And let me know, do you have any question about this solution ?
main_recyclerview.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
{
int position=rv.getChildAdapterPosition(rv.findChildViewUnder(e.getX(),e.getY()));
switch (position)
{
case 0:
{
wifi(position);
adapter2.notifyDataSetChanged();
}
break;
case 1:
{
sound(position);
adapter2.notifyDataSetChanged();
}
break;
case 2:
{
bluetooth(position);
adapter2.notifyDataSetChanged();
}
break;
}
return true;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e)
{
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
Instead of implementing interface View.OnClickListener inside view holder or creating and interface and implementing interface in your activity..
I used this code for simple on OnClickListener implementation.
public static class SimpleStringRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {
// Your initializations goes here...
private List<String> mValues;
public static class ViewHolder extends RecyclerView.ViewHolder {
//create a variable mView
public final View mView;
/*All your row widgets goes here
public final ImageView mImageView;
public final TextView mTextView;*/
public ViewHolder(View view) {
super(view);
//Initialize it here
mView = view;
/* your row widgets initializations goes here
mImageView = (ImageView) view.findViewById(R.id.avatar);
mTextView = (TextView) view.findViewById(android.R.id.text1);*/
}
}
public String getValueAt(int position) {
return mValues.get(position);
}
public SimpleStringRecyclerViewAdapter(Context context, List<String> items) {
mBackground = mTypedValue.resourceId;
mValues = items;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
view.setBackgroundResource(mBackground);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mBoundString = mValues.get(position);
holder.mTextView.setText(mValues.get(position));
//Here it is simply write onItemClick listener here
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, ExampleActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
}
use PlaceHolderView
#Layout(R.layout.item_view_1)
public class View1{
#View(R.id.txt)
public TextView txt;
#Resolve
public void onResolved() {
txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
}
#Click(R.id.btn)
public void onClick(){
txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
}
}
I wrote a library to handle android recycler view item click event. You can find whole tutorial in https://github.com/ChathuraHettiarachchi/RecycleClick
RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// YOUR CODE
}
});
or to handle item long press you can use
RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
#Override
public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
// YOUR CODE
return true;
}
});
recyclerview animation has not been tested, the other is normal. I think it has been optimized to the maximum. Interface has other uses, you can temporarily ignore.
public abstract class BaseAdapterRV<VH extends BaseViewHolder> extends RecyclerView.Adapter<VH> implements AdapterInterface {
public final String TAG = getClass().getSimpleName();
protected final Activity mActivity;
protected final LayoutInflater mInflater;
protected ItemClickInterface<?, Integer> mListener;
public BaseAdapterRV(Activity activity) {
mActivity = activity;
mInflater = LayoutInflater.from(mActivity);
}
#Override
public final VH onCreateViewHolder(ViewGroup parent, int viewType) {
return onCreateViewHolder(parent, viewType, mInflater);
}
#Override
public final void onBindViewHolder(VH holder, int position) {
holder.itemView.setTag(R.id.tag_view_click, position);
//创建点击事件
holder.itemView.setOnClickListener(mListener);
holder.itemView.setOnLongClickListener(mListener);
onBindVH(holder, position);
}
///////////////////////////////////////////////////////////////////////////
// 以下是增加的方法
///////////////////////////////////////////////////////////////////////////
/**
* 注意!涉及到notifyItemInserted刷新时立即获取position可能会不正确
* 里面也有onItemLongClick
*/
public void setOnItemClickListener(ItemClickInterface<?, Integer> listener) {
mListener = listener;
notifyDataSetChanged();
}
#NonNull
protected abstract VH onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater);
protected abstract void onBindVH(VH holder, int position);
}
This is Interface
/**
* OnItemClickListener的接口
* 见子类实现{#link OnItemClickListener}{#link OnItemItemClickListener}
*/
public interface ItemClickInterface<DATA1, DATA2> extends View.OnClickListener, View.OnLongClickListener {
void onItemClick(DATA1 data1, DATA2 data2);
boolean onItemLongClick(DATA1 data1, DATA2 data2);
}
This is an abstract class
public abstract class OnItemClickListener<DATA> implements ItemClickInterface<View, DATA> {
#Override
public void onClick(View v) {
onItemClick(v, (DATA) v.getTag(R.id.tag_view_click));
}
#Override
public boolean onLongClick(View v) {
return onItemLongClick(v, (DATA) v.getTag(R.id.tag_view_click));
}
#Override
public boolean onItemLongClick(View view, DATA data) {
return false;
}
}
You only need it
mAdapter.setOnItemClickListener(new OnItemClickListener<Integer>() {
#Override
public void onItemClick(View view, Integer integer) {
}
#Override
public boolean onItemLongClick(View view, Integer integer) {
return true;
}
});
I found one of the shortest ways using androidx lifecycle mutable live data
Adapter:
private val onItemClickListener = MutableLiveData<YourAdapterItem>()
override fun onBindViewHolder(holder: GifsViewHolder, position: Int) {
holder.itemView.setOnClickListener { onItemClickListener.value = gifs[position] }
}
fun getOnItemClickListener(): MutableLiveData<Gif> {
return onItemClickListener
}
anywhere in MainActivity
yourFancyAdapter.getOnItemClickListener().observe(this, Observer {
println(it)
})

Categories

Resources