117

I am replacing my ListView with RecyclerView, list showing ok, but I would like to know how to get clicked item and its position, similar to the method OnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id) we use in ListView.

Thanks for ideas!

Vasily Kabunov
  • 5,179
  • 12
  • 41
  • 46
Xcihnegn
  • 11,111
  • 10
  • 30
  • 32
  • 1
    check this tutorial http://wiki.workassis.com/android-recyclerview-example/ – Bikesh M Aug 30 '16 at 12:03
  • easiest way as for me: [CustomSelectionCallback](https://stackoverflow.com/questions/43720406/android-get-selectd-part-of-text-on-recyclerview-item-and-position) – ATES Jun 03 '20 at 12:54

22 Answers22

184

Based on the link: Why doesn't RecyclerView have onItemClickListener()? and How RecyclerView is different from Listview?, and also @Duncan's general idea, I give my solution here:

  • Define one interface RecyclerViewClickListener for a passing message from the adapter to Activity/Fragment:

      public interface RecyclerViewClickListener {
          public void recyclerViewListClicked(View v, int position);
      }
    
  • In Activity/Fragment implement the interface, and also pass listener to adapter:

      @Override
      public void recyclerViewListClicked(View v, int position){... ...}
    
      //set up adapter and pass clicked listener this
      myAdapter = new MyRecyclerViewAdapter(context, this);
    
  • In Adapter and ViewHolder:

      public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ItemViewHolder> {
         ... ... 
         private Context context;
         private static RecyclerViewClickListener itemListener;
    
    
         public MyRecyclerViewAdapter(Context context, RecyclerViewClickListener itemListener) {
             this.context = context;
             this.itemListener = itemListener;
             ... ...
         }
    
    
         //ViewHolder class implement OnClickListener, 
         //set clicklistener to itemView and, 
         //send message back to Activity/Fragment 
         public static class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
             ... ...
             public ItemViewHolder(View convertView) {
                 super(convertView);
                 ... ...
                 convertView.setOnClickListener(this);
             }
    
             @Override
             public void onClick(View v) {
                 itemListener.recyclerViewListClicked(v, this.getPosition());     
    
             }
         }
      }
    

After testing, it works fine.

[UPDATE]

Since API 22, RecyclerView.ViewHolder.getPosition() is deprecated, so instead with getLayoutPosition().

Noren
  • 30
  • 6
Xcihnegn
  • 11,111
  • 10
  • 30
  • 32
  • 24
    use getLayoutPosition() method as getPosition() is depricated now. – Ankur Chaudhary May 05 '15 at 06:28
  • 2
    thanks for this piece of code :) just one tip: reference the static `private static RecyclerViewClickListener itemListener;` in a static way in the constructor. – David Artmann Oct 04 '15 at 22:02
  • thank you for your help. how do we add swipe on the cards? – Parth Anjaria Nov 05 '15 at 13:21
  • What if I don't want ```RecyclerViewClickListener```to be static? For example, I have 3 tabs that use the same kind of adapter and the three implement RecyclerViewClickListener. But if it is not static, how to use it inside ```static class ItemViewHolde``` ? – Ferran Negre Dec 19 '15 at 17:33
  • @Ferran Negre this is exactly what i am looking for. Have you got the answer? – Khurram Shehzad Jan 02 '16 at 17:53
  • 1
    @khurramengr I ended up adding a listener into each item in the method ```onBindViewHolder```. Moreover, my list item was not all clickable (only part of the item). – Ferran Negre Jan 04 '16 at 00:33
  • If I want to use this Listener approach to delete items from the dataset and view. It works for the first item, but then the index is shifted by +1 every subsequent delete. I tried to use `adapter.notifyItemRemoved(pos)` but it doesn't help – 4ndro1d Apr 09 '16 at 19:09
  • how to get the clicked item object to activity from adapter android.i need more clearity am doing as per u r instruction item click listener is not called – Harsha Aug 22 '16 at 06:23
  • 1
    Should you not be using a WeakReference here: `new MyRecyclerViewAdapter(context, this)`? This might cause memory leaks – RamwiseMatt Feb 26 '17 at 01:08
  • 2
    `public void recyclerViewListClicked(View v, int position);` The modifier `public` is redundant for interface methods – Joel Raju May 20 '17 at 18:33
  • @Xcihnegn: can the "this.getPosition()" be replaced with "this.getAdapterPosition()"? – AJW Sep 03 '17 at 03:52
  • 1
    On onclick inside ItemViewHolderclass, we are passing itemListener.recyclerViewListClicked(v, this.getPosition());, instead you can pass recyclerviewItemClickListener.OnItemClick(view,getAdapterPosition());this as well. – Jay Dangar Oct 28 '18 at 09:01
37
public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.MyViewHolder>{
    public Context context;
    public ArrayList<RvDataItem> dataItems;

    ...
    constructor
    overrides
    ...

    class MyViewHolder extends RecyclerView.ViewHolder{
        public TextView textView;
        public Context context;

        public MyViewHolder(View itemView, Context context) {
            super(itemView);

            this.context = context;

            this.textView = (TextView)itemView.findViewById(R.id.textView);

            // on item click
            itemView.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    // get position
                    int pos = getAdapterPosition();

                    // check if item still exists
                    if(pos != RecyclerView.NO_POSITION){
                        RvDataItem clickedDataItem = dataItems.get(pos);
                        Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getName(), Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    }
}
Uday Hiwarale
  • 3,363
  • 4
  • 36
  • 44
13

Here is an Example to set a Click Listener.

Adapter extends  RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {  ...  }

 public static class MessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public TextView     tv_msg;
        public TextView     tv_date;
        public TextView     tv_sendTime;
        public ImageView    sharedFile;
        public ProgressBar sendingProgressBar;

        public MessageViewHolder(View v) {
            super(v);
            tv_msg =  (TextView) v.findViewById(R.id.tv_msg);
            tv_date = (TextView)  v.findViewById(R.id.tv_date);
            tv_sendTime = (TextView)  v.findViewById(R.id.tv_sendTime);
            sendingProgressBar = (ProgressBar) v.findViewById(R.id.sendingProgressBar);
            sharedFile = (ImageView) v.findViewById(R.id.sharedFile);

            sharedFile.setOnClickListener(this);

        }

        @Override
        public void onClick(View view) {
        int position  =   getAdapterPosition();


            switch (view.getId()){
                case R.id.sharedFile:


                    Log.w("", "Selected"+position);


                    break;
            }
        }

    }
Gilberto Ibarra
  • 2,299
  • 2
  • 21
  • 32
11

Put this code where you define recycler view in activity.

    rv_list.addOnItemTouchListener(
            new RecyclerItemClickListener(activity, new RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View v, int position) {

                    Toast.makeText(activity, "" + position, Toast.LENGTH_SHORT).show();
                }
            })
    );

Then make separate class and put this code:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
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) {

    }
}
Qantas 94 Heavy
  • 14,790
  • 31
  • 61
  • 78
bhumika rijiya
  • 466
  • 1
  • 5
  • 41
7

create java file with below code

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.getChildLayoutPosition(childView));
        return true;
    }
    return false;
}

@Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

}

and just use the listener on your RecyclerView object.

recyclerView.addOnItemTouchListener(  
new RecyclerItemClickListener(context, new RecyclerItemClickListener.OnItemClickListener() {
  @Override public void onItemClick(View view, int position) {
    // TODO Handle item click
  }
}));
MazRoid
  • 164
  • 1
  • 11
  • `RecyclerItemClickListener` is not a default android class, you should provide the library you are using or the class itself. – Greg Dec 21 '18 at 15:52
5

If you want Click event of recycle-View from activity/fragment instead of adapter then you can also use following short cut way.

recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                final TextView txtStatusChange = (TextView)v.findViewById(R.id.txt_key_status);
                txtStatusChange.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Log.e(TAG, "hello text " + txtStatusChange.getText().toString() + " TAG " + txtStatusChange.getTag().toString());
                        Util.showToast(CampaignLiveActivity.this,"hello");
                    }
                });
                return false;
            }
        });

You can also use other long ways like using interface

Amandeep Rohila
  • 3,302
  • 2
  • 24
  • 32
  • ,, hi there, but what if we have to get position of item clicked as well. ? – mfaisalhyder May 23 '16 at 05:58
  • You can just set the position of item in TAG through adapter and then get the tag as mentioned above txtStatusChange.getTag().toString() – Amandeep Rohila May 25 '16 at 06:35
  • @AmandeepRohila In order for the code to work, I have to scroll to the end of a horizontal recyclerview first. Only then does the toast work. Isn't there a way to make it work without having to scroll forth and back first? – iOSAndroidWindowsMobileAppsDev Nov 14 '16 at 09:48
4
recyclerViewObject.addOnItemTouchListener(
    new RecyclerItemClickListener(
        getContext(),
        recyclerViewObject,
        new RecyclerItemClickListener.OnItemClickListener() {
            @Override public void onItemClick(View view, int position) {
                // view is the clicked view (the one you wanted
                // position is its position in the adapter
            }
            @Override public void onLongItemClick(View view, int position) {
            }
        }
    )
);
rgmt
  • 13,342
  • 11
  • 43
  • 62
3

Use below code:-

public class SergejAdapter extends RecyclerView.Adapter<SergejAdapter.MyViewHolder>{

...

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{


    @Override
    public void onClick(View v) {
        // here you use position
        int position = getAdapterPosition();
        ...

    }
}
}
Akanksha Rathore
  • 3,445
  • 3
  • 28
  • 47
3

Here is the simplest and the easiest way to find the position of the clicked item:

I've also faced the same problem.

I wanted to find of the position of the clicked/selected item of the RecyclerView() and perform some specific operations on that particular item.

getAdapterPosition() method works like a charm for these kind of stuff. I found this method after a day of long research and after trying numerous other methods.

int position = getAdapterPosition();
Toast.makeText(this, "Position is: "+position, Toast.LENGTH_SHORT).show();

You do not have to use any extra method. Just create a global variable named 'position' and initialize it with getAdapterPosition() in any of the major method of the adapter (class or similar).

Here is a brief documentation from this link.

getAdapterPosition added in version 22.1.0 int getAdapterPosition () Returns the Adapter position of the item represented by this ViewHolder. Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet. RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.

Happy to help. Feel free to ask doubts.

2

My simple solution

Make a position holder:

    public class PositionHolder {

    private int position;

    public PositionHolder(int position) {
        this.position = position;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }
}

Just position or put data you need to get from activity.

Adapter constructor:

public ItemsAdapter(Context context, List<Item> items, PositionHolder positionHolder){
        this.context = context;
        this.items = items;
        this.positionHolder = positionHolder;
}

In Activity:

 @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        selectedPosition = 0;

        positionHolder = new PositionHolder(selectedPosition);
        initView();
    }

In Adapter onClickLictener in the item
in onBindViewHolder

holder.holderButton.setOnClickListener(v -> {
            positionHolder.setPosition(position);
            notifyDataSetChanged();
        });

Now whenever you change position in RecyclerView it is hold in the Holder (or maybe it should be called Listener)

I hope it will be usefull

My first post ;P

1

RecyclerView doesn't provide such method.

To manage click events on RecyclerView I ended up implementing onClickListener in my adapter, when binding the ViewHolder: In my ViewHolder I keep a reference to the root view (as you can do with your ImageViews, TextViews, etc...) and when binding the viewHolder I set a tag on it with information I need to handle click (such as position) and a clicklistener

Zoubiock
  • 1,165
  • 1
  • 8
  • 10
  • 3
    yes we can set click listener in ViewHolder, but I need pass back the clicked item view and position to my fragment for further process – Xcihnegn Feb 03 '15 at 11:03
1

Everytime I use another approach. People seem to store or get position on a view, rather than storing a reference to an object that is displayed by ViewHolder.

I use this approach instead, and just store it in ViewHolder when onBindViewHolder() is called, and set reference to null in onViewRecycled().

Every time ViewHolder becomes invisible, it's recycled. So this doesn't affect in large memory consumption.

@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
    ...
    holder.displayedItem = adapterItemsList.get(i);
    ...
}

@Override
public void onViewRecycled(ItemViewHolder holder) {
    ...
    holder.displayedItem = null;
    ...
}

class ItemViewHolder extends RecyclerView.ViewHolder {
    ...
    MySuperItemObject displayedItem = null;
    ...
}
Kirill Volkov
  • 932
  • 1
  • 9
  • 18
1

From the designer, you can set the onClick property of the listItem to a method defined with a single parameter. I have an example method defined below. The method getAdapterPosition will give you the index of the selected listItem.

public void exampleOnClickMethod(View view){
    myRecyclerView.getChildViewHolder(view).getAdapterPosition());
}

For information on setting up a RecyclerView, see the documentation here: https://developer.android.com/guide/topics/ui/layout/recyclerview

0
//Create below methods into the Activity which contains RecyclerView.


private void createRecyclerView() {

final RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    MyAdapter myAdapter=new MyAdapter(dataAray,MianActivity.this);
    recyclerView.setAdapter(myAdapter);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    setRecyclerViewClickListner(recyclerView);
}

private void setRecyclerViewClickListner(RecyclerView recyclerView){

    final GestureDetector gestureDetector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener() {
        @Override public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });


    recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
            View child =recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
            if(child!=null && gestureDetector.onTouchEvent(motionEvent)){
               int position=recyclerView.getChildLayoutPosition(child);
                String name=itemArray.get(position).name;

            return true;
        }

        @Override
        public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean b) {

        }
    });
}
Kamran
  • 11
  • 2
  • 3
    Please add an explanation, just dumping code without introduction and an explanation how this fixes the problem is not very helpful. – Mark Rotteveel Aug 16 '15 at 10:19
0

Try in this way

Adapter class :

 public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {

public interface OnItemClickListener {
    void onItemClick(ContentItem item);
}

private final List<ContentItem> items;
private final OnItemClickListener listener;

public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
    this.items = items;
    this.listener = listener;
}

@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
    return new ViewHolder(v);
}

@Override public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(items.get(position), listener);
}

@Override public int getItemCount() {
    return items.size();
}

static class ViewHolder extends RecyclerView.ViewHolder {

    private TextView name;
    private ImageView image;

    public ViewHolder(View itemView) {
        super(itemView);
        name = (TextView) itemView.findViewById(R.id.name);
        image = (ImageView) itemView.findViewById(R.id.image);
    }

    public void bind(final ContentItem item, final OnItemClickListener listener) {
        name.setText(item.name);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                listener.onItemClick(item);
            }
        });
    }
}

}

In your Activity or fragment :

ContentAdapter adapter = new ContentAdapter(itemList, this);

Note : Implement the OnItemClickListener based on context given by you in activity or fragment and overide methods.

RajeshVijayakumar
  • 9,551
  • 11
  • 53
  • 78
  • Nice solution above. Can you add to the above to show what the onItemClick method should look like in the Activity? I am trying to pass the item's position to an Intent (to open the next Activity) and I keep getting the wrong position. – AJW Sep 03 '17 at 04:22
0

Short extension for Kotlin
Method returns absolute position of all items (not the position of only visible items).

fun RecyclerView.getChildPositionAt(x: Float, y: Float): Int {
    return getChildAdapterPosition(findChildViewUnder(x, y))
}

And usage

val position = recyclerView.getChildPositionAt(event.x, event.y)
Pavel Shorokhov
  • 2,922
  • 1
  • 23
  • 33
0

Use getLayoutPosition() in your custom interface java method. This will return the selected position of an item, check full detail on

https://becody.com/get-clicked-item-and-its-position-in-recyclerview/

Rana Aamer
  • 21
  • 2
  • 1
    Welcome to Stack Overflow! While links are great way of sharing knowledge, they won't really answer the question if they get broken in the future. Add to your answer the essential content of the link which answers the question. In case the content is too complex or too big to fit here, describe the general idea of the proposed solution. Remember to always keep a link reference to the original solution's website. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer) – sɐunıɔןɐqɐp Aug 25 '18 at 22:29
0
//simply check if the adapter position you get not less than zero

holder.btnDelItem.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if(holder.getAdapterPosition()>=0){
            list.remove(holder.getAdapterPosition());
            notifyDataSetChanged();
        }
    }
});
Armel
  • 2,328
  • 6
  • 16
  • 28
  • 4
    When answering an old question, your answer would be much more useful to other StackOverflow users if you included some context to explain how your answer helps, particularly for a question that already has an accepted answer. See: [How do I write a good answer](https://stackoverflow.com/help/how-to-answer). – David Buck Nov 28 '19 at 10:41
0

After lot of trial and error I found that there is a very easy way to do this that is by creating an interface in adapter class and implementing that in fragment now here comes the twist, I instansiated the view model inside my override function present In the fragment now you can send the data from that function to viemodel and from there anywhere.

I don't know if this method is good coding method but please let me know in comment and if any wants to see the let me know in the comment section I regularly open stackover flow.

0
recyclerView.addOnItemTouchListener(object : AdapterView.OnItemClickListener,
                RecyclerView.OnItemTouchListener {
                override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                    TODO("Not yet implemented")
                }

                override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
                    TODO("Not yet implemented")
                }

                override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
                    TODO("Not yet implemented")
                }

                override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
                    TODO("Not yet implemented")
                }

            })

If use Kotlin

Sandeep Pareek
  • 300
  • 2
  • 7
  • 1
    While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Богдан Опир Jun 28 '20 at 07:39
0

The simple (but not so obvious) solution is to do this:

@Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

        View v = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.recycler_row, viewGroup, false);
        ViewHolder vh = new ViewHolder(v);

and then, whenever, call the method currentPosition = vh.getLayoutPosition();

In my case, I do that in an onClick listener put on that vh View. IMHO, the recycleView class misses out in that .getPosition() and other features that we know from ListView, and that are sometimes mandatory, are simply not available. I strongly regret having moved from ListView to Recycle ditto. The timethief cost was more than a day to unveil its mysteries. Bad engineering. (But what is there is ok)

carl
  • 483
  • 2
  • 9
-4

In onViewHolder set onClickListiner to any view and in side click use this code :

Toast.makeText(Drawer_bar.this, "position" + position, Toast.LENGTH_SHORT).show();

Replace Drawer_Bar with your Activity name.

Community
  • 1
  • 1