630

Has anyone using RecyclerView found a way to set an onClickListener to items in the RecyclerView? I thought of setting a listener to each of the layouts for each item but that seems a little too much hassle I'm sure there is a way for the RecyclerView to listen for the onClick event but I can't quite figure it out.

Eng.Fouad
  • 107,075
  • 62
  • 298
  • 390
CurtJRees
  • 6,318
  • 3
  • 12
  • 9

48 Answers48

623

Here is a better and less tightly coupled way to implement an OnClickListener for a RecyclerView.

Snippet of usage:

RecyclerView recyclerView = findViewById(R.id.recycler);
recyclerView.addOnItemTouchListener(
    new RecyclerItemClickListener(context, recyclerView ,new RecyclerItemClickListener.OnItemClickListener() {
      @Override public void onItemClick(View view, int position) {
        // do whatever
      }

      @Override public void onLongItemClick(View view, int position) {
        // do whatever
      }
    })
);

RecyclerItemClickListener implementation:

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);

    public void onLongItemClick(View view, int position);
  }

  GestureDetector mGestureDetector;

  public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null && mListener != null) {
                mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
            }
        }
    });
}

  @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 true;
    }
    return false;
  }

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

  @Override
  public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){}
}
Tom11
  • 2,199
  • 6
  • 26
  • 50
Jacob Tabak
  • 7,744
  • 3
  • 22
  • 32
  • 91
    This will not provide any clue about which button or view (within the item) was clicked. But for overall item click, this is fine. – Niranjan Oct 26 '14 at 06:38
  • what if i want to delete a row on its click ? – Piyush Kukadiya Nov 03 '14 at 06:26
  • You should have no issue deleting a row on click. The OnItemClickListener gives you the position of the view in the adapter so you can delete it. – Jacob Tabak Nov 03 '14 at 22:57
  • 27
    ngen's comment is correct - if you want to attach an onClick listener to a specific item within the item view, you should probably do that in your adapter, in onBindViewHolder, or in your viewholder itself. – Jacob Tabak Nov 10 '14 at 16:37
  • 16
    One problem with this method is when you are doing something that takes a few hundred milliseconds like starting an activity. When the item is clicked, and it has a clicked animation there is a noticeable delay between clicking and seeing the animation. – Gak2 Nov 12 '14 at 21:51
  • 1
    Can someone please explain the role of mGestureDetector.onTouchEvent(e) here and how it helps in determining the click? – Diffy Nov 14 '14 at 10:26
  • 21
    This solution feels extremely clunky, there's some delay when processing the 'click' and the feel isn't just ok – wasyl Dec 07 '14 at 23:54
  • Why is your code in OnInterceptTouchEvent instead of OnTouchEvent? – Andrew Orobator Jan 20 '15 at 06:09
  • Sorry, how do I use the `setItemChecked(position)` with your implementation? – Cris Apr 16 '15 at 09:52
  • 1
    @JacobTabak can we handle both click on individual views in adapter and item click listener. The touch is intercepted by onTouchListener. If i have both then i have both fire listeners one on view and the other on the row item – Raghunandan Apr 29 '15 at 08:21
  • @JacobTabak Note: view.getChildPosition(childView) is deprecated now – Arūnas Bedžinskas May 07 '15 at 10:34
  • @JacobTabak currently i add a method onLongPress(MotionEvent e) in the interface & in the GestureDetector i override onLogpress. (@Override public void onLongPress(MotionEvent e) { listener.onLongPress(e); }) i want to pass the child view and position , not the motion event to the listener like onItemClick.... How can i implement that.. – zacharia May 10 '15 at 11:46
  • 7
    Could anyone explain to me why this solution with the GestureDetector is supposed to be a better solution than just using "setOnClickListener" inside of onBindViewHolder() in the adapter to the according element? This way at least I need less code than with the GestureDetector solution. Thanks for an explanation. – Dominik Jun 01 '15 at 21:30
  • 2
    What do I use instead of view.getChildPosition as it has been depreciated? – Apurva Jun 19 '15 at 13:53
  • Simply use interfaces to avoid complexity. See https://gist.github.com/riyazMuhammad/1c7b1f9fa3065aa5a46f – Muhammad Riyaz Jul 22 '15 at 11:44
  • 1
    I just found out that this approach raises a fatal performance issue. There's great delay before the user could scroll the list(considering a list of several items). The user has to hold for sometime before scrolling in order for the RecyclerView to scroll, otherwise, it's just responding to the tough events. – Edward Quixote Aug 11 '15 at 15:50
  • 1
    getChildPosition is now deprecated and this does not tell you which view was clicked. Please see my answer below and https://gist.github.com/RTimal/ddf4040cfba28b461ec7 – Rajiev Timal Sep 05 '15 at 08:28
  • 1
    this method will not show the click effect on that item. Please follow below link https://plus.google.com/+AnjulaSashika/posts/D9jtomxRyDr – Anjula Sep 10 '15 at 02:49
  • 1
    You should also override `onRequestDisallowInterceptTouchEvent` event. – Naruto Uzumaki Nov 23 '15 at 14:27
  • For some reason it doesn't return a clicked view. In my case I click on a checkbox but given view is something different. The way I check: `view.getId() == R.id.selection` – dVaffection Nov 24 '15 at 08:01
  • I figured out the view is a topmost `Layout` for a `list_item.xml`. How can I know on which exactly `view` a click was? – dVaffection Nov 24 '15 at 08:08
  • 1
    getChildPosition() method is deprecated. Use getChildAdapterPosition(View) or getChildLayoutPosition(View). – Vikram Singh Jan 19 '16 at 16:28
  • 2
    @JacobTabak why this method is better than attaching onClickListener on individual Items ?? – veritas Feb 19 '16 at 04:31
  • 2
    This implementation introduces a few awkward edge cases since the touch events are divorced from the view they represent. Among them is click timing. GestureDetector supports long press by default, so a longer click is consumed and never reported—long press should be shut off. Another is the requirement of setting the view's visual clickability separately. This implementation doesn't connect with enabling the view to show this state to the user. – devunwired Apr 18 '16 at 17:56
  • 1
    Why are you taking a `Context` as a constructor parameter when you can just use `recyclerView.getContext()`? – zholmes1 Oct 14 '16 at 15:42
  • Another reason to avoid the Gesture Detector solution is that it doesn't work with keypad navigation. – BladeCoder Dec 20 '16 at 03:39
  • I have a checkbox in the card and when I use this class, for some reason holder.checkbox.setOnCheckedChangeListener does not get called. How to sort of bring the checkbox "to front"? – Zac Dec 20 '16 at 06:26
  • This solution does not consider focus navigation (keyboard navigation), something that a simple click listener could solve (click listeners encapsulates keyboard enter, D-Pad center, and other pointer events). This solution is **not accessible** as it can only handle touch events and shouldn't be used. Consider using a simple click listener with `getChildAdapterPosition()` to retrieve position. – Renato Jan 31 '17 at 00:45
  • When user click on button which is inside item view, how to handle that event? – Ajit Kumar Dubey May 10 '17 at 07:38
  • doesnt work on a list with one item. its item click rather than recycler click. – Amir Ziarati Apr 29 '18 at 06:45
  • @JacobTabak you are amazing, thanks so much for sharing. It worked for me :) – Tomas M Jan 16 '21 at 14:57
502

As the API's have radically changed, It wouldn't surprise me if you were to create an OnClickListener for each item. It isn't that much of a hassle though. In your implementation of RecyclerView.Adapter<MyViewHolder>, you should have:

private final OnClickListener mOnClickListener = new MyOnClickListener();

@Override
public MyViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
    View view = LayoutInflater.from(mContext).inflate(R.layout.myview, parent, false);
    view.setOnClickListener(mOnClickListener);
    return new MyViewHolder(view);
}

The onClick method:

@Override
public void onClick(final View view) {
    int itemPosition = mRecyclerView.getChildLayoutPosition(view);
    String item = mList.get(itemPosition);
    Toast.makeText(mContext, item, Toast.LENGTH_LONG).show();
}
nhaarman
  • 90,392
  • 51
  • 233
  • 265
  • 33
    what if i want to delete a row on its click ? – Piyush Kukadiya Nov 03 '14 at 06:19
  • 22
    I like this answer better than the one you linked. Who wants to write a gesture listener and hit box detection to handle this. Google-- – Lo-Tan Mar 23 '15 at 08:16
  • 17
    getChildPosition(view) is deprecated method – Jigar Apr 15 '15 at 13:48
  • 8
    we can use getChildLayoutPosition(view) instead. – Jigar Apr 15 '15 at 13:59
  • 2
    getChildAdapterPosition(view) is a more reliable option than getChildLayoutPosition(view) getChildLayoutPosition(view) may not be equal to Item's adapter position if there are pending changes in the adapter which have not been reflected to the layout yet. – Victor Odiah May 19 '15 at 04:49
  • 55
    Do you mind telling which classes are your snippets in? `onClick()`, belonging to `OnClickListener` can be attached to ANY View. The whole question here is which! Please make an effort, a method is not only identified by a name, but also at least by a class. If you don't say your code is to be added to the adapter, your answer does not really help. – Vince Jul 20 '15 at 18:58
  • 18
    Should the onClick method be in the fragment that holds the RecyclerView ? Because you reference the mRecyclerView in your MyOnClickListener class. Where did you get the reference to that object ? – Ced Jul 21 '15 at 20:06
  • getChildPosition is now deprecated, please see my answer below and https://gist.github.com/RTimal/ddf4040cfba28b461ec7 – Rajiev Timal Sep 05 '15 at 08:27
  • 5
    +1 for the fact that you specifically pointed out another author's method is less tightly coupled, and linked to it as _the first line_ of your answer. – jwir3 Sep 28 '15 at 16:52
  • 1
    `getChildPosition(View)` is **deprecated**. Use `public int getChildAdapterPosition (View child)` instead. Full documentation [here](https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#getChildAdapterPosition(android.view.View)) – Francois Dermu Oct 28 '15 at 17:23
  • 1
    `getChildPosition` is **deprecated** `getChildAdapterPosition` will give **wrong** position when you scroll the view. `getChildLayoutPosition` will give **correct** position. – Mehul Joisar Jan 20 '16 at 11:14
  • 12
    Where MyOnClickListener class should be written? – iYonatan Sep 16 '16 at 15:11
  • `public MyViewHolder onCreateViewHolder(final ViewGroup parent, final int position)` is confusing. The second argument is _viewType_ instead of _position_. The _position_ argument is only available in `onBindViewHolder(MyViewHolder holder, int position)` – tanghao Oct 01 '16 at 09:23
  • RecyclerView is pretty powerful even though you have to do the legwork yourself to handle the data and deal with any changes. +1 for the answer, perfecto! – Justin Mitchell Nov 14 '16 at 21:32
  • 4
    How to send `mRecyclerView` in an adapter ? – N Sharma Jun 06 '17 at 09:45
  • it's bad practice to implement click listener inside an onCreateView(). – Rissmon Suresh Jun 29 '17 at 22:34
  • 2
    Binding the click listener during ViewHolder creation is the correct way as it avoids repeated listener assignments that would happen if you did it in onBindViewHolder. However, the trick being missed here is that I also use ViewHolder to retain an ID (or some other 'handle' metadata) that provides means to determine the clicked item. This is instead of all the pointless jumping through hoops to calculate clicked Item position. So all I do is bind a click listener in the holder ctor, and have the holder also capture meta information that provides a pointer of some sort to the underlying data. – Trevor Dec 14 '17 at 12:37
  • 1
    I find it absolutely staggering that despite this being so simple to do as described above, some of the highest voted answers here advocate use of MotionEvents and various other bloat. – Trevor Dec 14 '17 at 12:42
  • 4
    where do this 'onClick' method belongs? How did you get 'mRecyclerView' inside that onClick method? – Shirish Herwade Jan 04 '18 at 11:28
  • what if the list has one item ? :/ i need recycler view click not item click – Amir Ziarati Apr 29 '18 at 06:40
  • @nhaarman can you help me to solve this https://stackoverflow.com/questions/50115252/how-to-load-fragments-when-a-user-clicks-on-a-cardview-recyclerview – Nikson May 01 '18 at 12:33
  • where does this 'onClick' method belong? How did you get 'mRecyclerView' inside that onClick method?... – Pemba Tamang May 07 '18 at 16:06
  • 1
    @PembaTamang @Shirish Herwade You can override the method `onAttachedToRecyclerView()` of your adapter, then you will get a reference to the recyclerView; set it to a member variable say `mRecyclerView`. And now, you got it. – mshsayem May 15 '18 at 14:41
  • why is it so confusing to just add a listener =.='' it's so much easier on iOS... Adding delegate – user1872384 Nov 09 '18 at 10:31
  • that `mRecyclerView` was bothering me (how do you access it inside adapter-class), so I modified the Adapter constructor `Adapter(..., RecyclerView rcv)` to accept the owner RV. Then I am able to access the element's position [inside onClick] `final int pos = myAdapter.this.myRecView.getChildAdapterPosition(view);` and it seems to work... – O-9 Jun 07 '19 at 10:59
  • Hi mate, can you help me to solve this issue please.. I've been stuck many days here https://stackoverflow.com/questions/57068321/how-do-we-handle-click-from-multiple-item-view-in-recyclerview – Sa'ad Abdurrazzaq Jul 21 '19 at 11:53
  • @Sa'adAbdurrazzaq No, and I'd appreciate it if you wouldn't ping people like this. – nhaarman Jul 21 '19 at 13:08
  • Because MANY people asked this: If I understand correctly, the onClick() method is inside the class "MyOnClickListener" he used for the click listener. The reference to mRecyclerView must have been passed as an argument to the constructor or so (which he unfortunately didn't show). please correct me if I'm wrong! – Hamburg is nice Mar 05 '21 at 21:16
120

I do it in this way, without undue classes, detectors etc. Simple code inside our adapter. Especially better solution for longClick than presented before.

public class PasswordAdapter extends RecyclerView.Adapter<PasswordAdapter.ViewHolder> {
    private static ClickListener clickListener;

    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
        TextView name;

        public ViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(this);
            itemView.setOnLongClickListener(this);
            name = (TextView) itemView.findViewById(R.id.card_name);
        }

        @Override
        public void onClick(View v) {
            clickListener.onItemClick(getAdapterPosition(), v);
        }

        @Override
        public boolean onLongClick(View v) {
            clickListener.onItemLongClick(getAdapterPosition(), v);
            return false;
        }
    }

    public void setOnItemClickListener(ClickListener clickListener) {
        PasswordAdapter.clickListener = clickListener;
    }

    public interface ClickListener {
        void onItemClick(int position, View v);
        void onItemLongClick(int position, View v);
    }
}

Then inside fragment or activity, just hit:

PasswordAdapter mAdapter = ...;

mAdapter.setOnItemClickListener(new PasswordAdapter.ClickListener() {
    @Override
    public void onItemClick(int position, View v) {
        Log.d(TAG, "onItemClick position: " + position);
    }

    @Override
    public void onItemLongClick(int position, View v) {
        Log.d(TAG, "onItemLongClick pos = " + position);
    }
});
Bakyt
  • 1,208
  • 11
  • 10
Marurban
  • 1,473
  • 1
  • 9
  • 9
  • instead of OurAdapter.clickListener i think Marurban means PasswordAdapter.clickListener and instead of TrainingAdapter.ClickListener(), should be PasswordAdapter.ClickListener(). Other than that, great solution Marurban! worked for me – nommer Aug 29 '15 at 00:30
  • onItemClick listener is also being activated on long click. – Rashad.Z Sep 30 '15 at 06:51
  • I had to use v.setClickListener(this) in ViewHolder constructor to make it work. But longclicklistener is still not working – Winster Aug 08 '16 at 05:37
  • 10
    ViewHolder must be static for the best performance. this is not a good answer. – Gilberto Ibarra Aug 24 '16 at 08:09
  • In the ViewHolder Class, when I set the onLongClick to return false, I fire off a longclick and both onItemClick and onItemLongClick get called. When I set the onLongClick to return true, only the correct click gets called, but the adapter doesnt update the recyclerview when I delete an item on a long click. Any Ideas as to what I'm doing wrong? – ChrisR Oct 24 '16 at 23:55
  • 11
    The static clickListener creates a memory leak. Declare the listener as non-static and bind it to the ViewHolder instead. – BladeCoder Dec 20 '16 at 03:43
  • ``return true`` from ``onLongClick`` to consume the event and avoid toast appearing for both click and long click. – Sumit Jha Jan 13 '17 at 05:11
  • Can you let me know if I had 2 buttons How can I get to know which button is clicked inside the Recyclerview? – user2695433 May 28 '17 at 17:51
  • You can check its id inside onClick method v.getId() – Marurban Jul 09 '17 at 19:45
  • @BladeCoder Can you provide an example of how best to bind the clickListener to the ViewHolder? Just assign it to the itemView of the clickListener? – AJW May 20 '19 at 05:47
  • 1
    @AJW The simplest way is to initialize the listener early and pass it as argument to the Adapter constructor and then the ViewHolder constructor. – BladeCoder May 20 '19 at 11:32
103

Check out a similar question @CommonsWare's comment links to this, which implements the OnClickListener interface in the viewHolder.

Here's a simple example of the ViewHolder:

    TextView textView;//declare global with in adapter class

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

      private ViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(this);
            textView = (TextView)view.findViewById(android.R.id.text1);

      }

      @Override
      public void onClick(View view) {
            Toast.makeText(view.getContext(), "position = " + getLayoutPosition(), Toast.LENGTH_SHORT).show();

         //go through each item if you have few items within recycler view
        if(getLayoutPosition()==0){
           //Do whatever you want here

        }else if(getLayoutPosition()==1){ 
           //Do whatever you want here         

        }else if(getLayoutPosition()==2){

        }else if(getLayoutPosition()==3){

        }else if(getLayoutPosition()==4){

        }else if(getLayoutPosition()==5){

        }

        //or you can use For loop if you have long list of items. Use its length or size of the list as 
        for(int i = 0; i<exampleList.size(); i++){

        }


      }
  }

The Adapter then looks like this:

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view =LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);

        return new ViewHolder(view);
    }
Josi
  • 887
  • 6
  • 9
bolot
  • 2,746
  • 2
  • 16
  • 15
  • 14
    This seems to go the wrong direction though. If you need to re-use a recyclerview in another fragment/activity, the adapter/viewholder are now defining the logic of the clicks instead of whatever is containing them. The clicks should really be handled directly or indirectly by the containing instance. It seems that a touch listener is the only effective way to go about this. – ian.shaun.thomas Oct 13 '14 at 14:49
  • 18
    @tencent I don't see how that is the problem. If the ViewHolder itself contains all information in regards to what data is selected when the element itself is clicked, then the only responsibility of the container is the display of N elements, rather than that of handling the selection of a specific i-th element out of the N elements. The `ListView` was already problematic if you had to create a list of more complicated objects than just simple rows. What if you need to handle different events depending on where you click the item in the ListView? You're screwed. Here, you have each listeners. – EpicPandaForce Apr 06 '15 at 15:00
  • 2
    I agree with @EpicPandaForce on this one. I also came to upvote this answer after reading the section of Commonsware's book on handling clicks in RecyclerViews It has worked great for me so far. – AdamMc331 Aug 17 '15 at 03:10
  • The whole point of a view holder is for it to hold the references to containing elements. If you use findViewById in onCreateViewHolder you're working around the intentional pattern. – Hubert Grzeskowiak Apr 09 '16 at 23:57
  • 1
    findViewById is only being used on view holder creation here (hence onCreateViewHolder). The view holder is intended to prevent finding the view every time an item is bound to the holder, not on initial creation. – stuckj Jun 15 '16 at 18:49
  • 2
    `getPosition()` is deprecated, use `getAdapterPosition()` or `getLayoutPosition()` instead. – K Neeraj Lal Sep 26 '16 at 07:02
  • Setting click for any part of view in `RecyclerView.ViewHolder` leads to incorrect processing of touch events. For example, if you would like to implement **SnapHelper** for such RecyclerView, `OnClickListener` will override `OnTouchListener` in **SnapHelper** and last one won't receive `MotionEvent.ACTION_DOWN` events. – Nick Sikrier Jan 15 '18 at 14:35
97

Based on Jacob Tabak's answer (+1 for him), I was able to add onLongClick listener:

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 {
    public interface OnItemClickListener {
        void onItemClick(View view, int position);

        void onItemLongClick(View view, int position);
    }

    private OnItemClickListener mListener;

    private GestureDetector mGestureDetector;

    public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
        mListener = listener;

        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());

                if (childView != null && mListener != null) {
                    mListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView));
                }
            }
        });
    }

    @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) {
    }
}

Then you can use it like this:

recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
    @Override
    public void onItemClick(View view, int position) {
        // ...
    }

    @Override
    public void onItemLongClick(View view, int position) {
        // ...
    }
}));
Benoit Duffez
  • 9,889
  • 11
  • 69
  • 114
Eng.Fouad
  • 107,075
  • 62
  • 298
  • 390
  • 1
    Took me a while to adapt to my app, and align and format code to Java/Android standards, but eventually it worked fine. – milosmns May 14 '15 at 16:59
  • How do you add an animation for the long click? – Jon Jun 17 '15 at 07:11
  • 1
    @Eng.Fouad click effect is not working by using this, although if we set click listener in adapter click effect works any solution to this – ingsaurabh Jul 24 '15 at 07:20
  • 4
    You should also override ```public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){}``` – Paolo Rotolo Sep 01 '15 at 10:29
  • For some reason it doesn't return a clicked view. In my case I click on a checkbox but given view is something different. The way I check: `view.getId() == R.id.selection` – dVaffection Nov 24 '15 at 08:02
  • I figured out the view is a topmost `Layout` for a `list_item.xml`. How can I know on which exactly `view` a click was? – dVaffection Nov 24 '15 at 08:08
  • getChildPosition() method is deprecated. Use getChildAdapterPosition(View) or getChildLayoutPosition(View). – Vikram Singh Jan 19 '16 at 16:28
68

This is what worked for me. Attach the OnClickListener to the onBindView. I don't really know if this will impact the performance, but it seems to work fine with little code.

public void onBindViewHolder(ViewHolder holder, final int position) {
    holder.view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, "Recycle Click" + position, Toast.LENGTH_SHORT).show();
            }
    });
}
Andrew T.
  • 4,592
  • 7
  • 38
  • 55
Bubunyo Nyavor
  • 2,331
  • 19
  • 33
  • 5
    in my case holder.itemView.setOnClickListener(..) – D4rWiNS Jul 23 '15 at 07:19
  • 19
    it is named 'itemView' instead of 'view' in my case, just saying for the beginners, some of them dont understand what are they copying, and can't figure out why doesnt work – D4rWiNS Jul 23 '15 at 07:50
  • Question: The onBindViewHolder(...) returns an int position. Which position is this in regards to the RecyclerView? Is it the Layout Position or Adapter Position? – Edward Quixote Aug 11 '15 at 16:11
  • 3
    Its the layout position – Bubunyo Nyavor Sep 16 '15 at 05:54
  • 1
    for me this seem to only apply for the last visible item not the clicked item – Nasz Njoka Sr. Dec 04 '15 at 12:00
  • 2
    This solution seems the best for me, but how does this differ from @bolot 's answer in therms of performance? He has implemented View.OnClickListener directly on the ViewHolder class, which than spits out the onClick method separately. Which would be the best solution? – Edmond Tamas Apr 24 '16 at 13:18
  • This unnecessarily creates a new OnClickListener constantly while scrolling and thus shouldn't be used. Use `getAdapterPosition()` to access the item position from the click listener. – 0101100101 Aug 09 '16 at 12:13
  • @0101100101 where do you do this. – Bubunyo Nyavor Aug 10 '16 at 09:31
  • @orignMaster `ViewHolder` has that method, so it can be used in any listener there. – 0101100101 Aug 10 '16 at 09:35
  • 1
    Total disaster. You'll get the item clicked position of the item which was just blinded not the item you click – Pablo Escobar Aug 14 '17 at 19:15
52

This was so hard for me to have on item click listener in the activity and also to have click listener for single view of the item that will not trigger on item click listener. After playing around with Jacob Tabak's answer I respect his answer for on item click if no other touch actions inside item are presented.

I have a custom OnClickListener interface that have on item click event which holds the clicked item's view and the item position from the adapter. I present an instance of it in the constructor(or it can be with setter) and attach it to the view holder container click listener.

I also have other click listener in the Adapter(Can be in the view holder) which will handle current View click from the container.

 public class MyRecyclerAdapter extends RecyclerView.Adapter<MyViewHolder> {

private ArrayList<String> mData;
private OnItemClickListener mOnItemClickListener;

public interface OnItemClickListener {
    public void onItemClick(View view, int position);
}

public MyRecyclerAdapter(ArrayList<String> itemsData,
        OnItemClickListener onItemClickListener) {
    mOnItemClickListener = onItemClickListener;
    this.mData = itemsData;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent,
        int viewType) {

    View layoutView = LayoutInflater.from(mContext).inflate(
            R.layout.list_item, parent, false);

    final MyViewHolder viewHolder = new MyViewHolder(layoutView);

    viewHolder.container.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            mOnItemClickListener.onItemClick(v, viewHolder.getAdapterPosition());
        }
    });

    viewHоlder.button.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            //do button click work here with
            // mData.get( viewHolder.getAdapterPosition() );
        }
    });

    return viewHolder;
}

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

In the activity you need to initialize the adapter by passing instance of the OnItemClickListener

public class FeedActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

    .....

    MyRecyclerAdapter adapter = new MyRecyclerAdapter(new ArrayList<String>(), new OnItemClickListener() {

        @Override
        public void onItemClick(View view, int position) {

            ///list item was clicked
        }
    });

    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(mFeedsAdapter);
}

And my ViewHolder

public class MyViewHolder extends RecyclerView.ViewHolder {

public Button button;
public View container;

public MyViewHolder(View itemLayoutView) {
    super(itemLayoutView);

    container = itemLayoutView;
    button = (Button) itemLayoutView.findViewById(R.id.button);
}}
  • This approach worked well for me - it means you can handle clicks in the activity rather than inside the grid view – leafcutter Apr 19 '15 at 21:11
  • 3
    Where can I give you money. 4 hours of searching and then I read your post. Thanks for the time you took to post this. – Brandon Jul 30 '15 at 20:21
  • 3
    This sounds stupid but, does this have any memory or efficiency like issue? Because we are assigning the click listener every time a view is being drawn right? – rgv Aug 20 '15 at 03:40
  • 1
    @Raghav yes every time view is being bind a new click listener is assigned, but this is the standard flow. But the mOnItemClickListener is created only once for the adapter just the view and its position are different on each item click ;) – Sir NIkolay Cesar The First Sep 24 '15 at 18:53
  • Thanks, this really helped me understand how to implement OnClickListener Interfaces – markfknight Sep 30 '15 at 23:12
  • container should be WeakReference. The Context (your activity) contains a reference to the recycler view, which contains a reference to the view holder which, in this case, has a reference back to the context. – W.K.S Jan 22 '17 at 11:11
  • @W.K.S the container is different for each item and is needed to assign the onclick listener to it. The RecyclerView should recycle the ViewHolder if it's needed... – Sir NIkolay Cesar The First Jan 24 '17 at 11:07
  • 3
    Great example to kill app performance and create memory overuse! With this example you just need scroll to create hundreds of useless objects, that in a moment will be useless and have to be accessed as garbige. @NoobDogg is right. No ClickListeners haven't to be created at onBindView. A click listener have to be created once in onCreateView (or holder constructor), and exists while we need screen with this adapter. – Mykola Tychyna Sep 11 '17 at 06:57
  • @MykolaTychyna how will you perform click listener for an item, when you don't have it's position on onCreateView?! You need to know which item was clicked and have it's position so you can access it's data. You are just not right – Sir NIkolay Cesar The First Oct 30 '17 at 08:43
  • @Sir NIkolay Cesar The First please at first notice the problem of creation of huge numbers of objects (ClickListeners) with very short life time. Its overhead and antipattern - always! When you are running onCreateViewHolder your are running ViewHolder constructor, and inside it you have to create click lstener. Inside of method onClick you can call getAdapterPosition and perform with this index get on your data collection. In this case ClickListeners will be created as much as ViewHolders. – Mykola Tychyna Oct 31 '17 at 06:44
  • To get the item position inside onCreateView (so that the click listener may be bound there), you simply need to have another property inside your ViewHolder that captures the index, or some other metadata that provides a 'handle' to that item, during onBindViewHolder. This is all I do. There is no need for repeated binding of listeners, and there is no need to jump through any hoops to calculate the clicked item. I'm absolutely staggered that none of these answers illustrate this. – Trevor Dec 14 '17 at 12:32
  • How to pass the holder values through intent? – RoCk RoCk May 23 '18 at 11:41
  • Ok I editted my answer as I see that some people are still liking it ;) – Sir NIkolay Cesar The First Nov 04 '19 at 17:09
40

This is what I ended up needing, in case someone finds it useful:

public static class ViewHolder extends RecyclerView.ViewHolder {

    public ViewHolder(View item) {

        super(item);
        item.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("RecyclerView", "onClick:" + getAdapterPosition());
            }
        });

    }
}

Source: http://blog.csdn.net/jwzhangjie/article/details/36868515

Fifer Sheep
  • 2,590
  • 2
  • 25
  • 40
32

I have nice solution for RecyclerView's onItemClickListener for the items and subitems

Step 1- Create an interface

public interface OnRecyclerViewItemClickListener
{
    /**
     * Called when any item with in recyclerview or any item with in item
     * clicked
     * 
     * @param position
     *            The position of the item
     * @param id
     *            The id of the view which is clicked with in the item or
     *            -1 if the item itself clicked
     */
    public void onRecyclerViewItemClicked(int position, int id);
}

Step 2- Then use it in adapter's onBindViewHolder method in the following way

/**
     * Custom created method for Setting the item click listener for the items and items with in items
     * @param listener OnRecyclerViewItemClickListener 
     */
    public void setOnItemClickListener(OnRecyclerViewItemClickListener listener)
    {
        this.listener = listener;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position)
    {
    
        // viewHolder.albumBg.setBackgroundResource(_itemData[position]
        // .getImageUrl());
    
        viewHolder.albumName.setText(arrayList.get(position).getName());
        viewHolder.artistName.setText(arrayList.get(position).getArtistName());
        String imgUrl = arrayList.get(position).getThumbImageUrl();
    
        makeImageRequest(imgUrl, viewHolder);
        viewHolder.parentView.setOnClickListener(new View.OnClickListener()
        {
    
            @Override
            public void onClick(View v)
            {
                listener.onRecyclerViewItemClicked(position, -1);
            }
        });
        viewHolder.settingButton.setOnClickListener(new View.OnClickListener()
        {
    
            @Override
            public void onClick(View v)
            {
                listener.onRecyclerViewItemClicked(position, v.getId());
            }
        });
    
    }
    
    // class to hold a reference to each item of RecyclerView
    public static class ViewHolder extends RecyclerView.ViewHolder
    {
    
        public TextView albumName, artistName;
        public ImageView albumIcon, settingButton;
        public LinearLayout parentView;
    
        public ViewHolder(View itemLayoutView)
        {
            super(itemLayoutView);
            // albumBg = (LinearLayout) itemLayoutView
            // .findViewById(R.id.albumDlbg);
            albumName = (TextView) itemLayoutView.findViewById(R.id.albumName);
            artistName = (TextView) itemLayoutView
                    .findViewById(R.id.artistName);
            albumIcon = (ImageView) itemLayoutView.findViewById(R.id.albumIcon);
            parentView = (LinearLayout) itemLayoutView
                    .findViewById(R.id.albumDlbg);
            settingButton = (ImageView) itemLayoutView
                    .findViewById(R.id.settingBtn);
        }
    
    }

Step 3- find and setup recycler view in activity or fragment where you are using this

recyclerView = (RecyclerView) rootview.findViewById(R.id.vmtopsongs);

        lm = new LinearLayoutManager(mActivity);
        lm.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(lm);
        recyclerView.addItemDecoration(
                new HorizontalDividerItemDecoration.Builder(getActivity())
                        .paint(Utils.getPaint()).build());
        PopularSongsadapter mAdapter = new PopularSongsadapter(gallery,
                mActivity, true);
        // set adapter
        recyclerView.setAdapter(mAdapter);
        mAdapter.setOnItemClickListener(this);
        // set item animator to DefaultAnimator
        recyclerView.setItemAnimator(new DefaultItemAnimator());

Step 4- Finally implement interface in activity or fragment where you are using the recyclerview

@Override
    public void onRecyclerViewItemClicked(int position, int id)
    {
        if(id==-1){
            Toast.makeText(mActivity, "complete item clicked", Toast.LENGTH_LONG).show();
        }else{
            Toast.makeText(mActivity, "setting button clicked", Toast.LENGTH_LONG).show();
        }
    }

Update for Kotlin Language I have updated the code for kotlin in which only whole view has on click listener. You can set subitems click listener by editing interface and code according to above java code.

Adapter

class RecentPostsAdapter(private val list: MutableList<Post>) :
    RecyclerView.Adapter<RecentPostsAdapter.ViewHolder>() {
    private lateinit var onItemClickListener: OnItemClickListener

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(parent.context)
                .inflate(R.layout.listitem_recent_post, parent, false)
        )
    }

    override fun getItemCount(): Int {
        return list.size
    }

    fun setOnItemClickListener(onItemClickListener: OnItemClickListener) {
        this.onItemClickListener = onItemClickListener
    }

    private fun getItem(position: Int): Post {
        return list[position]
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
        holder.itemView.setOnClickListener(View.OnClickListener {
            onItemClickListener.onItemClick(
                position
            )
        })
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private var imageView: NetworkImageView? = null
        private var tvTitle: TextView? = null
        private var tvExcerpt: TextView? = null
        private var htmlSpanner: HtmlSpanner = HtmlSpanner()

        init {
            imageView = itemView.findViewById(R.id.niv_post_image)
            tvTitle = itemView.findViewById(R.id.tv_post_title)
            tvExcerpt = itemView.findViewById(R.id.tv_post_excerpt)
        }

        fun bind(post: Post) {
            tvTitle?.text = post.title
            tvExcerpt?.text = htmlSpanner.fromHtml(post.excerpt)
        }
    }

    interface OnItemClickListener {
        fun onItemClick(position: Int)
    }
}

Activity or Fragment

recyclerView = view.findViewById(R.id.rvHomeRecentPosts)
        recyclerView.layoutManager = LinearLayoutManager(view.context)
        list = mutableListOf()
        recentPostsAdapter = RecentPostsAdapter(list)
        recyclerView.adapter = recentPostsAdapter
        recentPostsAdapter.setOnItemClickListener(object:RecentPostsAdapter.OnItemClickListener{
            override fun onItemClick(position: Int) {
                (activity as MainActivity).findNavController(R.id.nav_host_fragment).navigate(R.id.action_nav_home_to_nav_post_detail)
            }

        })
Gopal Singh Sirvi
  • 3,923
  • 5
  • 25
  • 47
  • 2
    This is the best solution because we usually need a lot of properties from activity/fragment on our onItemClickListener, and theres no sense to call it direct from the viewholder (i would have to create a custom constructor for each adapter). By the way, your code is a little extense, i suggest you to cut it to the most important part (setOnItemClickListener). – sagits Oct 28 '15 at 16:37
  • this is a good simple approach for differentiating the row / item from the sub-items – Nigel Savage May 20 '17 at 23:49
  • I'm listened that it is bad solution because you create too many listeners. – Egor Randomize Jul 14 '20 at 04:18
  • It will create as many listeners as many items are visible on the screen. As we scroll the unused listeners will be garbage collected – Gopal Singh Sirvi Jul 14 '20 at 12:05
  • Other way is to add a tag to the view, we can use the position number as tag and create only one listener and set it to all items. Then the position will be found out by getting the tag – Gopal Singh Sirvi Jul 14 '20 at 12:07
17

Here is what I did. This solution supports both onClick and onLongClick on both RecyclerView Items and Views insides RecyclerView Items (internal views).

I tag viewHolder on the views of my choice :

public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, null);
    ViewHolder viewHolder = new ViewHolder(itemView);

    itemView.setOnClickListener( this);
    itemView.setOnLongClickListener(this);
    viewHolder.imageIV.setOnClickListener(this);
    viewHolder.imageIV.setOnLongClickListener(this);

    viewHolder.imageIV.setTag(viewHolder);
    itemView.setTag(viewHolder);

    return viewHolder;
}

And I use holder.getPosition() to retrieve the position in onClick() method (onLongClick is similar) :

public void onClick(View view) {
    ViewHolder holder = (ViewHolder) view.getTag();
    int position = holder.getPosition();

    if (view.getId() == holder.imageIV.getId()){
        Toast.makeText(context, "imageIV onClick at" + position, Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(context, "RecyclerView Item onClick at " + position, Toast.LENGTH_SHORT).show();
    }
}

A variant with getChildPosition also works. Please note that for the internal views, in onClick() use :

int position = recyclerView.getChildPosition((View)view.getParent());

To my mind, the avantage of this solution is that when one clicks on the image, only the onclick() image listener is called whereas when I combined Jacob's solution for a RecyclerView Item view and my solution for internal views the RecyclerView Item view onclick() is also called (when click on image).

u2gilles
  • 4,628
  • 4
  • 35
  • 59
  • There are some other ways to get a reference to the full list row View, using the ViewHolder: 1 - `ViewGroup rowView = (ViewGroup)viewHolder.imageIV.getParent()`. 2 - `View rowView = viewHolder.itemView`. Then you can add `onClickListeners` to these `rowViews`. – Mr-IDE Nov 30 '20 at 20:20
11

There is far easier way to do this. Just apply on click in onBindViewHolder on root view.

Consider this is your view for adapter,

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/linearlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

        <TextView
            android:id="@+id/textview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="1dp"
            android:textSize="15sp" />
</LinearLayout>

Then do following in your adapter

//get the layout and make view holder
@Override
public RVAdapter.ViewHolder1 onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_layout, null);
    ViewHolder1 viewHolder = new ViewHolder1(view);
    return viewHolder;
}

@Override
public void onBindViewHolder(RVAdapter.ViewHolder1 holder, int position) {

    //apply on click on your root view
    holder.linearlayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //Do on click stuff
        }
    });
}

//make references to views in layout including root view
public class ViewHolder1 extends RecyclerView.ViewHolder {

    protected LinearLayout linearlayout = null
    protected TextView textview = null;

    public CareerLinksViewHolder(View itemView) {
        super(itemView);

        this.linearlayout = (LinearLayout) itemView.findViewById(R.id.linearlayout);
        this.tvCompName = (TextView) itemView.findViewById(R.id.textview);
    }
}
Sushant
  • 1,248
  • 1
  • 14
  • 31
8

You can pass a clickListener to Adapter.

In your Activity:

private View.OnClickListener mItemClick = new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        Intent intent = null;
        int position = list.getChildPosition(v);
        switch (position) {
            case 0:
                intent = new Intent(MainActivity.this, LeakCanaryActivity.class);
                break;
            case 1:
                intent = new Intent(MainActivity.this, ButterKnifeFragmentActivity.class);
                break;
        }
        if (intent != null) {
            MainActivity.this.startActivity(intent);
        }
    }
};

then pass it to Adapter:

MainAdapter mainAdapter = new MainAdapter(this, mItemClick);

In Adapter's onCreateViewHolder:

 @Override
public MainAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
    View itemView = activity.getLayoutInflater().inflate(R.layout.main_adapter_item, viewGroup, false);
    ViewHolder holder = new ViewHolder(itemView);
    itemView.setOnClickListener(mItemClick);
    return holder;
}
Tom11
  • 2,199
  • 6
  • 26
  • 50
Francis Shi
  • 345
  • 4
  • 7
7

You can implement OnClickListener to your ViewHolder class

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public Item item
        @InjectView(R.id.tv_title)
        public TextView tvTitle;
        @InjectView(R.id.rl_row)
        public RelativeLayout rlRow;

        public ViewHolder(View v) {
            super(v);
            ButterKnife.inject(this, v);
            v.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            Log.e("item title",item.getTitle());
        }
    }

And onBindViewHolder set your view holder's item

public void onBindViewHolder(ViewHolder holder, int position) {
        holder.tvTitle.setText(objects.get(position).getTitle());
        holder.item = objects.get(position);
        }
savepopulation
  • 10,674
  • 4
  • 47
  • 68
7

If you want to catch click event On Individual items then just implement OnClickListener in ViewHolder class and then set click listeners on individual views or whole itemView.

Following example shows the same

public  class ContactViewHolder extends RecyclerView.ViewHolder implements OnClickListener
    {
        TextView txt_title,txt_name,txt_email;

        public ContactViewHolder(View itemView) 
        {
            super(itemView);
            txt_title = (TextView)itemView.findViewById(R.id.txt_title);
            txt_name  = (TextView)itemView.findViewById(R.id.txt_name);
            txt_email = (TextView)itemView.findViewById(R.id.txt_email);

            txt_name.setOnClickListener(this);
            txt_email.setOnClickListener(this);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            if(v == itemView)
            {
                Toast.makeText(RecyclerDemoActivity.this, "Visiting Card Clicked is ==>"+txt_name.getText(), Toast.LENGTH_SHORT).show();
            }

            if(v == txt_name)
            {
                Toast.makeText(RecyclerDemoActivity.this, "Name ==>"+txt_name.getText(), Toast.LENGTH_SHORT).show();
            }

            if(v == txt_email)
            {
                Toast.makeText(RecyclerDemoActivity.this, "Email ==>"+txt_email.getText(), Toast.LENGTH_SHORT).show();
            }
        }

    }
} 
Abdullah
  • 6,605
  • 6
  • 23
  • 39
HemangNirmal
  • 601
  • 8
  • 23
7

Way too simple and effective.

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();
        }
    }
Tom11
  • 2,199
  • 6
  • 26
  • 50
Manikanta
  • 3,201
  • 4
  • 24
  • 48
  • 3
    This is a common bad practice because it looks way too easy. You are creating a new inner listener each time an object is binded (and each item in a recycler can be bound lots of times) which is bad for a)performance b)garbage collection. Instead you should create ONE listener somewhere (in the viewholder, in the adapter, in the upper fragment or activity) and just add it to the items in the viewholder constructor on in onCreateViewHolder. This has the added complexity of having to figure out which item was clicker, but can be solved easily via tags or adding more info to the holder. – GuillermoMP Feb 20 '16 at 22:16
7

I have developed a light weighted library for android, you can visit https://github.com/ChathuraHettiarachchi/RecycleClick

and follow for following sample

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() {
            @Override
            public void onItemClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
            }
        });
6

All the answers posted so far are great solutions, however if you do not want to deal with too many implementation details, and just want it to work similarly to how ListView does, I would recommend using TwoWay-View, as seen here:

https://github.com/lucasr/twoway-view

Note that this implementation also supports long press on items, as well as support for pressed states (which is something important that other solutions to this question lack).

If you don't want to use the entire library, take a look at the ClickItemTouchListener class, which can be used as a standalone if needed. The only issue I found with it at the moment is with long press + scrolling, it seems to have incorrect behaviour.

Jawnnypoo
  • 963
  • 11
  • 18
5

For me, this is the best way:

class YourRecyclerAdapter extends RecyclerView.Adapter<ContactViewHolder> implements View.OnClickListener { 
  ...
  @Override
  public void onClick(View view) {
        int itemPosition = vRecycle.getChildPosition(view);
        //And use itemPosition to get the item from your collection. This way you dont restrain the ViewHolder with a OnClick callback
    }
  ...
}
4gus71n
  • 4,133
  • 2
  • 32
  • 55
5

The RecyclerView does not have a OnClickListener and will have to implement it ourselves.

I like to add a OnItemClickListener interface in Adapter with an onClick method invoked when you click on the item view from the ViewHolder. Thus the responsibility of managing the click on an item is outside the ViewHolder and Adapter. Will the activity or fragment which will decide what to do

Add an interface to the listener and the listener object.

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

  ...

  private static OnItemClickListener onItemClickListener;

  ...

  public static interface OnItemClickListener {
      public void onItemClick(View view, int position);
  }

  ...
}

We capture the click of the root view of the item and when the callback is triggered onClick listener call on the adapter .

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

  ...

  private static OnItemClickListener onItemClickListener;

  ...

  public static interface OnItemClickListener {
      public void onItemClick(View view, int position);
  }

  ...

  public static class ViewHolder extends RecyclerView.ViewHolder {
      public ImageView imageView;

      public ViewHolder(View itemRootView) {
          super(itemRootView);
          imageView = (ImageView) itemRootView.findViewById(R.id.itemImage);

          itemRootView.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  int position  = ViewHolder.super.getAdapterPosition();
                  onItemClickListener.onItemClick(view,position);
              }
          });
      }
  }
}

Since the activity or fragment , fragment in our case , we assign a listener to the adapter and the onClick callback we will get the selected item by position and opened a detailed activity of item.

public class ItemsFragment extends Fragment {
    ...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
       ...    
        ((ItemsAdapter) adapter).setOnItemClickListener(new ItemsAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                //Do something when an item has been clicked
            }
        });
        ...
    }
...
}
Tom11
  • 2,199
  • 6
  • 26
  • 50
xurxodev
  • 1,281
  • 18
  • 15
5

Here is what I did Read more & download the gist here

Adding the same here

CustomItemClickListener.java

public interface CustomItemClickListener {
 public void onItemClick(View v, int position);
}

ItemsListAdapter.java

public class ItemsListAdapter extends RecyclerView.Adapter<ItemsListAdapter.ViewHolder> {
ArrayList<ItemListSingleItem> data;

Context mContext;
CustomItemClickListener listener;

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.items_list_single_item, parent, false);
    final ViewHolder mViewHolder = new ViewHolder(mView);
    mView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            listener.onItemClick(v, mViewHolder.getAdapterPosition());
        }
    });
    return mViewHolder;
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    holder.itemTitle.setText(Html.fromHtml(data.get(position).getTitle()));
    if (!TextUtils.isEmpty(data.get(position).getThumbnailURL())) {
      // I Love picasso library :) http://square.github.io/picasso/
        Picasso.with(mContext).load(data.get(position).getThumbnailURL()).error(R.drawable.ic_no_image).
                placeholder(R.drawable.ic_no_image).
                transform(new RoundedCornersTransformation(5, 0)).
                into(holder.thumbnailImage);
    } else {
        holder.thumbnailImage.setImageResource(R.drawable.ic_no_image);
    }
}


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

public ItemsListAdapter(Context mContext, ArrayList<ItemsListSingleItem> data, CustomItemClickListener listener) {
    this.data = data;
    this.mContext = mContext;
    this.listener = listener;
}

public static class ViewHolder extends RecyclerView.ViewHolder {
    public TextView itemTitle;
    public ImageView thumbnailImage;

    ViewHolder(View v) {
        super(v);
        itemTitle = (TextView) v
                .findViewById(R.id.post_title);
        thumbnailImage = (ImageView) v.findViewById(R.id.post_thumb_image);
    }
 }
}
MyNT
  • 18
  • 3
Muhammad Riyaz
  • 2,372
  • 1
  • 22
  • 27
5

Unfortunately RecyclerView is missing a couple of features that ListView had built-in. For example the ability to add an OnItemClickListener that triggers when an item is clicked. RecyclerView allows you to set an OnClickListener in your adapter, but passing on that click listener from your calling code, to the adapter and to the ViewHolder, is complicated for catching a simple item click.

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) {
            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) {
        if (mOnItemClickListener != null) {
            view.setOnClickListener(mOnClickListener);
        }
        if (mOnItemLongClickListener != null) {
            view.setOnLongClickListener(mOnLongClickListener);
        }
    }

    @Override
    public void onChildViewDetachedFromWindow(View view) {

    }
};

private ItemClickSupport(RecyclerView recyclerView) {
    mRecyclerView = recyclerView;
    mRecyclerView.setTag(R.id.item_click_support, this);
    mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}

public static ItemClickSupport addTo(RecyclerView view) {
    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);
}
}

You also need to define R.id.item_click_support using ids.xml:

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
  <item name="item_click_support" type="id" />
 </resources>

The resulting code click listener now looks like this:

ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
    // do it
}
});

For Brief Explanation about recyclerview clicks please have a look at this littlerobots_blog

Tom11
  • 2,199
  • 6
  • 26
  • 50
anand krish
  • 3,029
  • 4
  • 32
  • 43
5

you can easily define setOnClickListener in your ViewHolder class as follow :

public class ViewHolder extends RecyclerView.ViewHolder {
    TextView product_name;

    ViewHolder(View itemView) {
        super(itemView);
        product_name = (TextView) itemView.findViewById(R.id.product_name);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int itemPosition = getLayoutPosition();
                Toast.makeText(getApplicationContext(), itemPosition + ":" + String.valueOf(product_name.getText()), Toast.LENGTH_SHORT).show();
            }
        });
    }
}
Arash Hatami
  • 4,313
  • 4
  • 31
  • 50
5

Kotlin implementation of nhaarman's answer :

mRecyclerView.addOnItemTouchListener(object  : RecyclerItemClickListener(this, mRecyclerView,object :RecyclerItemClickListener.OnItemClickListener{
            override fun onItemClick(view: View, position: Int) {

            }

            override fun onLongItemClick(view: View?, position: Int) {

            }
}){})

RecyclerItemClickListener.java :

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View


open class RecyclerItemClickListener(context: Context, recyclerView: RecyclerView, private val mListener: OnItemClickListener?) : RecyclerView.OnItemTouchListener {

    private var mGestureDetector: GestureDetector

    interface OnItemClickListener {
        fun onItemClick(view: View, position: Int)

        fun onLongItemClick(view: View?, position: Int)
    }

    init {
        mGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
            override fun onSingleTapUp(e: MotionEvent): Boolean {
                return true
            }

            override fun onLongPress(e: MotionEvent) {
                val child = recyclerView.findChildViewUnder(e.x, e.y)
                if (child != null && mListener != null) {
                    mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child))
                }
            }
        })
    }

    override fun onInterceptTouchEvent(view: RecyclerView, e: MotionEvent): Boolean {
        val childView = view.findChildViewUnder(e.x, e.y)
        if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
            mListener.onItemClick(childView, view.getChildAdapterPosition(childView))
            return true
        }
        return false
    }

    override fun onTouchEvent(view: RecyclerView, motionEvent: MotionEvent) {}

    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
}
Nilesh Deokar
  • 2,777
  • 27
  • 47
4

From most of the answers above, they seem to be setting their onclicklisteners to individual items. However, the solution am about to offer is very simple but yet not intuitive to many. Many are forgetting that the other components are always in a parent component which is used to display items in the List or Recycler views. This solution is just about setting a single onclick listener to this parent view and the turn is played. The solution also includes a way to pass the position of the item being clicked on from the list or recycler view. Here, our main rootview is a CardView from the android support library. Here is sample code

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

public static final String LOG_TAG = ListAdapter.class.getSimpleName();
private Cursor mDataset;
private Context mContext;
private ViewHolder mViewHolder;

// Provide a suitable constructor (depends on the kind of dataset)
public ListAdapter(Context context, Cursor Dataset) {
    mDataset = Dataset;
    mContext = context;
}

// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    // create a new view
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.list_business_view, parent, false);

    mViewHolder = new ViewHolder(v);
    return mViewHolder;
}

public void setData(Cursor newdata) {
    this.mDataset = newdata;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//Bind data to other items here. To save time, i have ommited that.
           //here is where we attach a click listerner for an item in the recycler list rather than for each element of a given item.
            holder.card.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, " Just cliked item at position " + itemPosition, Toast.LENGTH_LONG).show();

            }
        });

    }
}

// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
    if (null != mDataset) {
        return mDataset.getCount();
    }
    return 0;

}


// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class ViewHolder extends RecyclerView.ViewHolder{
    // each data item is just a string in this case
    public final TextView mBusinesssName; // View for the business name
    public final TextView mBusinessCategory; //View for the category name
    public final ImageView businessImage; // View for the business category image Image
    public final TextView mBusinessDistance; // View for the distance
    public final CardView card;

    public ViewHolder(View view) {
        super(view);
        mBusinesssName = (TextView) view.findViewById(R.id.list_item_name_textview);
        mBusinessCategory = (TextView) view.findViewById(R.id.list_item_category_textview);
        mBusinessDistance = (TextView) view.findViewById(R.id.list_item_dist_textview);
        businessImage = (ImageView) view.findViewById(R.id.list_item_icon);
        card = (CardView) view.findViewById(R.id.card_view);

    }
}
}
Akah
  • 1,160
  • 12
  • 17
3

here is complete code for my custom adapter this code will inflate the rows with list items defined in the xml file named "list_item" it will also perform click event on all list items rows with respective positions.

public class MyCustomAdapter extends RecyclerView.Adapter`<`AdapterMyCustomAdapter.ViewHolder> {

    public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
        public onItemClickListener mListener;
        public ViewHolder(View v, onItemClickListener listener) {
            super(v);
            mListener =listener;
            v.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            mListener.onRecyclerItemClick(v, getPosition());
        }

        public static interface onItemClickListener {
            public void onRecyclerItemClick(View view , int position);
        }
    }

    @Override
    public int getItemCount() {
        return 5;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int pos) {      

    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item, parent, false);

    /* here list_item is an xml file we want to inflate ...it is same as we do in case of listview for customization.*/

        MyCustomAdapter.ViewHolder vh = new ViewHolder(v, new MyCustomAdapter.ViewHolder.onItemClickListener() {

            @Override
            public void onRecyclerItemClick(View view, int position) {
                System.out.println("clicked on list item at position " +position);
            } 
        });
        return vh;
    }
}
Karthikeyan
  • 168
  • 2
  • 10
Lone_iqbal
  • 31
  • 4
3

I'm aware there are a lot of answers, but I thought I might just provide my implementation of it as well. (Full details can be found on another question I answered).

So, to add a click listener, your inner ViewHolder class needs to implement View.OnClickListener. This is because you will set an OnClickListener to the itemView parameter of the ViewHolder's constructor. Let me show you what I mean:

public class ExampleClickViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    TextView text1, text2;

    ExampleClickViewHolder(View itemView) {
        super(itemView);

        // we do this because we want to check when an item has been clicked:
        itemView.setOnClickListener(this);

        // now, like before, we assign our View variables
        title = (TextView) itemView.findViewById(R.id.text1);
        subtitle = (TextView) itemView.findViewById(R.id.text2);
    }

    @Override
    public void onClick(View v) {
        // The user may not set a click listener for list items, in which case our listener
        // will be null, so we need to check for this
        if (mOnEntryClickListener != null) {
            mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
        }
    }
}

The only other things you need to add are a custom interface for your Adapter and a setter method:

private OnEntryClickListener mOnEntryClickListener;

public interface OnEntryClickListener {
    void onEntryClick(View view, int position);
}

public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
    mOnEntryClickListener = onEntryClickListener;
}

So your new, click-supporting Adapter is complete.

Now, let's use it...

    ExampleClickAdapter clickAdapter = new ExampleClickAdapter(yourObjects);
    clickAdapter.setOnEntryClickListener(new ExampleClickAdapter.OnEntryClickListener() {
        @Override
        public void onEntryClick(View view, int position) {
            // stuff that will happen when a list item is clicked
        }
    });

It's basically how you would set up a normal Adapter, except that you use your setter method that you created to control what you will do when your user clicks a particular list item.

You can also look through a set of examples I made on this Gist on GitHub:

https://gist.github.com/FarbodSalamat-Zadeh/7646564f48ee708c1582c013e1de4f07

Community
  • 1
  • 1
Farbod Salamat-Zadeh
  • 18,039
  • 16
  • 66
  • 118
3
public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView title, year, genre;

        public MyViewHolder(View view) {
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            genre = (TextView) view.findViewById(R.id.genre);
            year = (TextView) view.findViewById(R.id.year);
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(context, ""+getAdapterPosition(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
2

We can do this using Java weak references. Semantically, the view holder is the one that should respond to the click event or delegate it to the correct responder.

Our goals:

  1. The Viewholder should know nothing about the the class thats responding to the events except that it implements a certain interface.
  2. The click handler should get the position in the RecyclerView of the view thats clicked.
  3. We should be able to discern which view was clicked in the view holder.
  4. Maintain loose coupling between all components and don't cause any retain cycles.

Steps:

  1. Create an interface to handle click responses.

  2. Implement this interface in the Activity that will handle the click.

  3. Add a member variable in the RecyclerView Adapter to hold the Weak Reference and a constructor that sets it.

  4. Do the same in the RecyclerView ViewHolder and add a member variable to keep track of position.

  5. Set your on click listeners on any view you'd like in the ViewHolder, then callback to the responder to handle them.

  6. Change your onBindViewHolder method to set the position when binding.

  7. Pass the responder down to the ViewHolder.

  8. In the responder, you can now use getId() on the view to figure out which view was clicked.

And here's a Gist so you can see how it all fits together: RecyclerView click handling

2

For me the clean way to do that is this one.

Adapter constructor

private class EnvironmentTypeRecyclerViewAdapter extends RecyclerView.Adapter<EnvironmentTypeRecyclerViewAdapter.ViewHolder>
{
     private final EnvironmentTypeRecyclerViewAdapterListener mEnvironmentTypeRecyclerViewAdapterListener;
     private List<Environment> mEnvironmentsData;

     public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
     {
         public ViewHolder(View v)
         {
             super(v);
             v.setOnClickListener(this);

         }

         @Override
         public void onClick(View v)
         {
              Environment environment = mEnvironmentsData.get(getAdapterPosition());
              if (mEnvironmentTypeRecyclerViewAdapterListener != null && environment != null) {
                      mEnvironmentTypeRecyclerViewAdapterListener.onListItemSelected(environment);      
              }
        }

        public EnvironmentTypeRecyclerViewAdapter(List<SmallCellEnvironment> environments, EnvironmentTypeRecyclerViewAdapterListener environmentTypeRecyclerViewAdapterListener)
        {
            mEnvironmentTypeRecyclerViewAdapterListener = environmentTypeRecyclerViewAdapterListener;
            mEnvironmentsData = environments;
        }
}

The Linked Interface

private interface EnvironmentTypeRecyclerViewAdapterListener
{
    void onListItemSelected(Environment environment);
}
Karthikeyan
  • 168
  • 2
  • 10
2

This is what I do to reuse OnClickListener

  public class TestAdapter extends RecyclerView.Adapter<TestAdapter.MyviewHolder>
                                         implements View.OnClickListener

in ViewHoder Take itemlayout's parent

  public class MyviewHolder extends RecyclerView.ViewHolder {

       LinearLayout linearLayout_item;

        public MyviewHolder(View itemView) {
            super(itemView);
            linearLayout_item=itemView.findViewById(R.id.linearLayout_item);
        }
    }

in onBindViewHolder set tag as position

   @Override
    public void onBindViewHolder(MyviewHolder holder, int position) {

       holder.linearLayout_item.setTag(position);
       holder.linearLayout_item.setOnClickListener(this);
    }

and in Onclick

 @Override
public void onClick(View v) {

    int position = (int) v.getTag();
    switch (v.getId()) {
        case R.id.linearLayout_item:

            // do some thing with position 

            break;
    }
}
Manohar Reddy
  • 15,894
  • 7
  • 77
  • 109
2

In kotlin with constructor implementation

Initialize your Recyclerview constructor like below

class ListAdapter(
c: Context,
private var list: List<Project>,
private val itemClick: (Project) -> Unit
) : RecyclerView.Adapter<ListAdapter.ViewHolder>() 

return with itemClick in your onCreateViewHolder

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):ProjectViewHolder {
    val view = inflater.inflate(R.layout.list_item, parent, false)
    return ViewHolder(view, itemClick)
}

Your onBindViewHolder

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.bindProject(list[position])
}

Then with viewHolder class create bindProject function.

class ViewHolder(
    view: View,
    private val itemClick: (Project) -> Unit
) : RecyclerView.ViewHolder(view) {

    private val clientTextCount = 7

    val titleTextView: TextView = view.projectTitleTextView

    fun bindProject(project: Project) {
        with(project) {
            titleTextView.text = name
            itemView.setOnClickListener { itemClick(this) }
        }
    }
}

Finally in your activity initialize adapter with lazy

private val adapter: ListAdapter by lazy {
    ListAdapter(this, projectList, {
        // Here you can implement your onClick function.
    })
}

For adding new list to recyclerview initialize below method in your adapter

fun setProjects(projects: List<Project>) {
        projectList = projects
        notifyDataSetChanged()
    }

and call setProjects method anywhere in your activity.

adapter.setProjects(projects)

That's it.

Naveen
  • 340
  • 2
  • 12
  • I have the same approach, but an item click is not triggered until a 2-3 extra clicks on the same line item. Any idea why extra clicks are required? – Charan Mar 21 '21 at 12:03
2

recyclerview with click listener

Add an interface in your adapter class.

public interface SelectedUser{

    void selectedUser(UserModel userModel);

}

implement your interface in the mainactivity and overide selectedUser method.

@Override
public void selectedUser(UserModel userModel) {

    startActivity(new Intent(MainActivity.this, SelectedUserActivity.class).putExtra("data",userModel));

}

Full tutorial check out here plus source code. Recyclerview with clicklistener and searchview

Richard Kamere
  • 428
  • 9
  • 8
1

Try this, simple enough. It works for me. BTW, I found that setOnClickListener doesn't take effect for recycler view.

    recycler.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                // anything todo
            }
            return true;
        }
    });
ManuQiao
  • 650
  • 7
  • 19
1

Here is simple and clear way is add inside your ReacyclerView ViewHolder

public static class MyViewholder extends RecyclerView.ViewHolder {

    public MyViewholder(View itemView) {
        super(itemView);

        itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Tag", "onClick:" + getAdapterPosition());
            }
        });

    }
}

getAdapterPosition() is returns the current clicked item position

Vipul Prajapati
  • 1,017
  • 7
  • 11
1

According to Yigit Boyar, the best way to register a click on a RecyclerView is to define the click in the creation of the viewholder instead of just creating a new onClickListener for each item that the onBindViewHolder binds

Example

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
        val itemBinding = LayoutInflater.from(context).inflate(R.layout.my_layout, parent, false)
        val vh = MainViewHolder(itemBinding)

        vh.itemView.setOnClickListener {
            val pos = vh.adapterPosition
            if(pos != NO_POSITION){
                itemClickLister.onCocktailClick(myList[pos],pos)
            }
        }
    return vh
}
Gastón Saillén
  • 9,076
  • 4
  • 39
  • 58
0

Mark the class as abstract and implement an OnClick method

public abstract class MainGridAdapter extends
    RecyclerView.Adapter<MainGridAdapter.ViewHolder> {
private List<MainListItem> mDataset;

// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder {
    // each data item is just a string in this case
    public TextView txtHeader;
    public TextView txtFooter;

    public ViewHolder(View v) {
        super(v);
        txtHeader = (TextView) v.findViewById(R.id.firstLine);
        txtFooter = (TextView) v.findViewById(R.id.secondLine);
    }
}

public void add(int position, MainListItem item) {
    mDataset.add(position, item);
    notifyItemInserted(position);
}

public void remove(MainListItem item) {
    int position = mDataset.indexOf(item);
    mDataset.remove(position);
    notifyItemRemoved(position);
}

// Provide a suitable constructor (depends on the kind of dataset)
public MainGridAdapter(List<MainListItem> myDataset) {
    mDataset = myDataset;
}

// Create new views (invoked by the layout manager)
@Override
public MainGridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
        int viewType) {
    // create a new view
    View v = LayoutInflater.from(parent.getContext()).inflate(
            R.layout.list_item_grid_line, parent, false);
    // set the view's size, margins, paddings and layout parameters
    ViewHolder vh = new ViewHolder(v);
    return vh;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    // - get element from your dataset at this position
    // - replace the contents of the view with that element     
    OnClickListener clickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            onItemClicked(position);
        }
    };
    holder.itemView.setOnClickListener(clickListener);
    holder.txtHeader.setOnClickListener(clickListener);
    holder.txtFooter.setOnClickListener(clickListener);
    final MainListItem item = mDataset.get(position);
    holder.txtHeader.setText(item.getTitle());
    if (TextUtils.isEmpty(item.getDescription())) {
        holder.txtFooter.setVisibility(View.GONE);
    } else {
        holder.txtFooter.setVisibility(View.VISIBLE);
        holder.txtFooter.setText(item.getDescription());
    }
}

// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
    return mDataset.size();
}

public abstract void onItemClicked(int position);

}

Implement click handler in binding event to only have one event implementation

Implementation of this:

mAdapter = new MainGridAdapter(listItems) {         
    @Override
    public void onItemClicked(int position) {
        showToast("Item Clicked: " + position, ToastPlus.STYLE_INFO);
    }
};

Same can be done for long click

Mr G
  • 131
  • 1
  • 5
0

Here is my Code Snippet

v.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) 
        {
            int newposition = MainActivity.mRecyclerView.getChildAdapterPosition(v);
            Intent cardViewIntent = new Intent(c, in.itechvalley.cardviewexample.MainActivityCards.class);
            cardViewIntent.putExtra("Position", newposition);
            c.startActivity(cardViewIntent);
        }
    });

v is View from onCreateViewHolder

c is Context

zackygaurav
  • 3,941
  • 3
  • 23
  • 38
0

Here is a strategy that gives a result similar to the ListView implementation in that you can define the listener in the Activity or Fragment level instead of the Adapter or ViewHolder level. It also defines some abstract classes that take care of a lot of the boilerplate work of adapters and holders.

Abstract Classes

First, define an abstract Holder that extends RecyclerView.ViewHolder and defines a generic data type, T, used to bind data to the views. The bindViews method will be implemented by a subclass to map data to the views.

public abstract class Holder<T> extends RecyclerView.ViewHolder {
    T data;

    public Holder(View itemView) {
        super(itemView);
    }

    public void bindData(T data){
        this.data = data;
        bindViews(data);
    }

    abstract protected void bindViews(T data);
}

Also, create an abstract Adapter that extends RecyclerView.Adapter<Holder<T>>. This defines 2 of the 3 interface methods, and a subclass will need to implement the last, onViewHolderCreated method.

public abstract class Adapter<T> extends RecyclerView.Adapter<Holder<T>> {
    List<T> list = new ArrayList<>();

    @Override
    public void onBindViewHolder(Holder<T> holder, int position) {
        holder.bindData(list.get(position));
    }

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

    public T getItem(int adapterPosition){
        return list.get(adapterPosition);
    }
}

Concrete Classes

Now create a new concrete class that extends Holder. This method only has to define the Views and handle the binding. Here I'm using the ButterKnife library, but feel free to use itemView.findViewById(...) methods instead.

public class PersonHolder extends Holder<Person>{
    @Bind(R.id.firstname) TextView firstname;
    @Bind(R.id.lastname) TextView lastname;

    public PersonHolder(View view){
        super(view);
        ButterKnife.bind(this, view);
    }

    @Override
    protected void bindViews(Person person) {
        firstname.setText(person.firstname);
        lastname.setText(person.lastname);
    }
}

Finally, in your Activity or Fragment class that holds the RecyclerView you would have this code:

// Create adapter, this happens in parent Activity or Fragment of RecyclerView
adapter = new Adapter<Person>(){
    @Override
    public PersonHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.layout_person_view, parent, false);

        PersonHolder holder = new PersonHolder(v);
        v.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                int itemPos = holder.getAdapterPosition();
                Person person = getItem(itemPos);

                // do something with person
                EventBus.getDefault().postSticky(new PersonClickedEvent(itemPos, person));
            }
        });

        return holder;
    }
};
bcorso
  • 40,568
  • 8
  • 56
  • 73
0

Very simple, add this class:

public class OnItemClickListener implements View.OnClickListener {
    private int position;
    private OnItemClickCallback onItemClickCallback;

    public OnItemClickListener(int position, OnItemClickCallback onItemClickCallback) {
        this.position = position;
        this.onItemClickCallback = onItemClickCallback;
    }

    @Override
    public void onClick(View view) {
        onItemClickCallback.onItemClicked(view, position);
    }

    public interface OnItemClickCallback {
        void onItemClicked(View view, int position);
    }
}

Get an instance of 'OnItemClickCallback' interface and put it in your activity or fragment:

private OnItemClickListener.OnItemClickCallback onItemClickCallback = new OnItemClickListener.OnItemClickCallback() {
    @Override
    public void onItemClicked(View view, int position) {
    }
};

Then, pass that callback to your recyclerView:

recyclerView.setAdapter(new SimpleStringRecyclerViewAdapter(Arrays.asList("1", "2", "3"), onItemClickCallback));

Finally, this would be your adapter:

public class SimpleStringRecyclerViewAdapter extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {
    private List<String> mValues;
    private OnItemClickListener.OnItemClickCallback onItemClickCallback;

    public SimpleStringRecyclerViewAdapter(List<String> items, OnItemClickListener.OnItemClickCallback onItemClickCallback) {
        mValues = items;
        this.onItemClickCallback = onItemClickCallback;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public final TextView mTextView;

        public ViewHolder(View view) {
            super(view);
            mTextView = (TextView) view.findViewById(R.id.txt_title);
        }
    }

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

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        holder.mTextView.setText(mValues.get(position));
        holder.mTextView.setOnClickListener(new OnItemClickListener(position, onItemClickCallback));
    }

    @Override
    public int getItemCount() {
        return mValues.size();
    }
}
Hamzeh Soboh
  • 7,070
  • 5
  • 37
  • 54
0

Usually you have more than one element in your CardView, so that you need an layout view to wrap and organize them.
You can add an OnClickListener to that layout view.
1. Add an id to your layout. In this case a LinearLayout

<android.support.v7.widget.CardView
 .....>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/card_view_linearLayout">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="name"
            android:id="@+id/card_view_name" />

        ...

    </LinearLayout>

</android.support.v7.widget.CardView>

$ 2. Get the layout view in your inner ViewHolder class.

public static class ViewHolder extends RecyclerView.ViewHolder{
    private TextView nameView;
    ...
    private LinearLayout linearLayout;
    public ViewHolder(View itemView) {
        super(itemView);
        nameView = (TextView)itemView.findViewById(R.id.card_view_name);
        ...
        linearLayout = (LinearLayout)itemView.findViewById(R.id.card_view_linearLayout);
    }
}

$ 3. Add the listener to your layout in onBindViewHolder and use a callback to send data to the Activity or Fragment(not tested).

@Override
public void onBindViewHolder(TrackAdapter.ViewHolder holder, final int position) {
    String str = mStringList.get(position);

    holder.nameView.setText(str);
    ...
    holder.linearLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            callback.itemCallback(mStringList.get(position));
        }
    });
}

how to use callbacks is another story

Shashanth
  • 4,151
  • 7
  • 32
  • 46
RickPat
  • 477
  • 6
  • 6
0

Step 1 ) Write the click interface

Create an interface named RecyclerViewClickListener.java and add below code. Here we declare two methods onClick and onLongClick to identify item click and long click respectively.

package com.androidtutorialshub.recyclerviewtutorial.Helper;

import android.view.View;

public interface RecyclerViewClickListener {
    void onClick(View view, int position);

    void onLongClick(View view, int position);
}

Step 2 ) Write the Item Touch Class

Create a class named RecyclerViewTouchListener.java and add below code . Here we write the logic to detect click and long press on recycler view item .

package com.androidtutorialshub.recyclerviewtutorial.Helper;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;


public class RecyclerViewTouchListener implements RecyclerView.OnItemTouchListener{

    private GestureDetector gestureDetector;
    private RecyclerViewClickListener clickListener;

    public RecyclerViewTouchListener(Context context, final RecyclerView recyclerView, final RecyclerViewClickListener clickListener) {
        this.clickListener = clickListener;
        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if (child != null && clickListener != null) {
                    clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                }
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

        View child = rv.findChildViewUnder(e.getX(), e.getY());
        if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
            clickListener.onClick(child, rv.getChildPosition(child));
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
}

Step 3 ) Defining click listener

Open MainActivity.java and update the below changes. Here onClick() method will detect click on item and onLongClick will detect long click on item.

recyclerView.addOnItemTouchListener(new RecyclerViewTouchListener(getApplicationContext(), recyclerView, new RecyclerViewClickListener() {
            @Override
            public void onClick(View view, int position) {
                Toast.makeText(getApplicationContext(), bookList.get(position).getTitle() + " is clicked!", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onLongClick(View view, int position) {
                Toast.makeText(getApplicationContext(), bookList.get(position).getTitle() + " is long pressed!", Toast.LENGTH_SHORT).show();

            }
        }));

For more info or Download source code :- http://www.androidtutorialshub.com/android-recyclerview-click-listener-tutorial/

lalit vasan
  • 432
  • 3
  • 6
  • its click on the on the whole item. if i have one more button in this to perform functionality on this. it's also treat like as itemview click listener? – Arslan Maqbool Sep 12 '18 at 10:05
0

Same answer in Kotlin

inner class MyViewHolder(v: View, myOnClickListener: MyOnClickListener) : RecyclerView.ViewHolder(v) {
    init {
        v.setOnClickListener { v -> myOnClickListener.onClick(v, adapterPosition) }
    }
}

override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MyViewHolder {
    val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.myview, viewGroup, false)
    return MyViewHolder(view, mOnClickListener)
}

inner class MyOnClickListener {
    fun onClick(view: View, position: Int) {
        val item = mList[position]
        Toast.makeText(view.context, item, Toast.LENGTH_LONG).show()
    }
}
ayvazj
  • 3,865
  • 1
  • 11
  • 12
0

I have looked thorugh all of the anwers and was not quite satasifed. I found much easier and faster solution. Wanted to share for future readers.

  1. Choose any View inside your single recycler item.
  2. Get parent of this View (Make sure you cast to appropirate ViewGroup )
  3. Set your onClickListener to this parent.

Sample code (It is written inside your onBindViewHolder method of your adapter ):

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {

    ConstraintLayout parent = (ConstraintLayout) holder.title.getParent();
    parent.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(context, "Clicked recycler view item at position " + position, Toast.LENGTH_SHORT).show();
        }
    });
}
0

For kotlin handle click on recyclerview anser based on Jacobs anser

kotlin RecyclerViewClick #RecyclerViewClick # Kotlin

1) CREATE CLASS RecyclerItemClickListener

class RecyclerItemClickListener(context: Context, recyclerView: RecyclerView, listner: OnItemClickListener) : RecyclerView.OnItemTouchListener {
    var mGestureDetector: GestureDetector
    var mListner: OnItemClickListener
    interface OnItemClickListener {
        fun onItemClick(view: View, position: Int)
        fun onLongItemClick(view: View, position: Int)
    }
    init {
        this.mListner = listner
        mGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
            override fun onSingleTapUp(e: MotionEvent?): Boolean {
                return true
            }
            override fun onLongPress(e: MotionEvent?) {
                val child: View? = recyclerView.findChildViewUnder(e!!.getX(), e.getY())
                if (child != null && mListner != null) {
                    mListner.onLongItemClick(child, recyclerView.getChildAdapterPosition(child))
                }

            }

        })
    }
    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
    }

    override fun onInterceptTouchEvent(view: RecyclerView, e: MotionEvent): Boolean {
        val childView: View? = view.findChildViewUnder(e!!.getX(), e.getY())
        if (childView != null && mListner != null && mGestureDetector.onTouchEvent(e)) {
            mListner.onItemClick(childView, view.getChildAdapterPosition(childView))
            return true
        }
        return false
    }

    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
    }

}

2) get Click on Any Recyclerview (activity/fragment)

 recyclerView.addOnItemTouchListener(
            RecyclerItemClickListener(
                this, recyclerView, object : RecyclerItemClickListener.OnItemClickListener {
                    override fun onItemClick(view: View, position: Int) {

                    }

                    override fun onLongItemClick(view: View, position: Int) {
                    }
                })
        )
Community
  • 1
  • 1
jai khambhayta
  • 2,263
  • 12
  • 24
0

Let's see how we can also implement this with Jetpack / AndroidX

You need to create an observable in the viewmodel class like this

private MutableLiveData<Integer> adapterItem = new MutableLiveData<>();

public MutableLiveData<Integer> getAdapterItem() {
    return adapterItem;
}

public void setAdapterItem(int adapterItem) {
    this.getAdapterItem().setValue(adapterItem);
}

Then in the adapter class make sure you pass the viewmodel reference as parameter in the constructor then implement the clicklistener on the viewholder

    public MyViewHolder(@NonNull View itemView) {
        super(itemView);
        if(itemView != null){
            itemView.setOnClickListener(v -> {
                int adapterPosition = getAdapterPosition();
                viewModel.setAdapterItem(adapterPosition);
            });

        };
    }

Then from the activity class just observe the changes

    viewModel.getAdapterItem().observe(this, position -> {
        Log.w(TAG, "clicked: " + ridesArray.get(position));
    });
Roman Gherta
  • 738
  • 2
  • 11
  • 23
  • Hi Jimmy, the rule was that you should not pass view references to the viewmodel if you don't want memory leaks. In this case we are passing viewmodel to adapter that is managing views. I tried to think of situations under which this would create a memory leak but unfortunately I cannot think of any. VIewmodel lifecycle is larger than adapter and thus I do not think this would create a memory leak. If you have something on your mind, do let me know. I do not claim the absolute truth. – Roman Gherta Oct 23 '19 at 17:40
0

here my way

in activity class:

    public class MyActivity extends AppCompatActivity implements EmployeeAdapter.ClickListener {
    ...
    @Override
    public void onClick(int position) { ... }
    ...
    }

in adapter class:

    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    ...
    @Override
    public void onBindViewHolder(){
         holder.textView1.setOnClickListener(v -> clickListener.onClick(position));
    }
    ...
    public interface ClickListener {
        void onClick(int position);
    }
    ...
    }
vitalinvent
  • 305
  • 1
  • 3
  • 9
0

Don't reinvent the wheel! The code for this specific use case is included in the Master/Detail Flow starter project that comes with Android Studio.

From Android Studio select:

  1. File > New > New Project....
  2. In the Phone and Tablet tab select Master/Detail Flow as shown below.

enter image description here

  1. Create the project as either Kotlin or Java.
  2. Profit.

I am not going to include here the code from google's ootb the demo project, but I'll outline the main design approaches in the sample provided by google:

  • the item OnClickListener is created ONLY ONCE, and is assigned to a field in your RecyclerView.Adapter implementation.
  • in the onBindViewHolder() you should set the same, pre-created onClickListener object on your ViewHolder instance with holder.itemView.setOnClickListener(mOnClickListener) (AVOID creating a new instance on every method call!); if you need to capture clicks on some specific elements inside the ViewHolder then extend ViewHolder and expose the elements you need as fields so that you can attach whatever listeners you need in onBindViewHolder() — and once again, do NOT re-create the listeners on every method call — initialise them as instance fields and attach them as needed.
  • you can use .setTag() in order to pass state to your viewHolder, e.g. holder.itemView.setTag(mValues.get(position)); as used in the demo.
ccpizza
  • 21,405
  • 10
  • 121
  • 123
-1

This works.

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

private final Context mContext;
private List<ServiceListModel> categoryList;
private View.OnClickListener onClickListener;

public ServiceListAdapter(Context mContext, List<ServiceListModel> categoryList, View.OnClickListener onClickListener) {
    this.categoryList = categoryList;
    this.mContext = mContext;
    this.onClickListener = onClickListener;
}

@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
    final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    final RowServiceListBinding binding = DataBindingUtil.inflate(inflater, R.layout.row_service_list, parent, false);
    return new ViewHolder(binding.getRoot(), binding);
}

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    holder.binding.rlService.setOnClickListener(onClickListener);
    holder.binding.rlService.setTag(position);
}

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

public class ViewHolder extends RecyclerView.ViewHolder {
    private final RowServiceListBinding binding;

    public ViewHolder(final View view, final RowServiceListBinding binding) {
        super(view);
        this.binding = binding;
    }

    @UiThread
    public void bind(final ServiceListModel mAddressModel) {
        //this.binding.setAddress(mAddressModel);
    }
}
}

Use in activity/fragment

ServiceListAdapter adapter = new ServiceListAdapter(context, serviceList, new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.rlService:
                    int pos = (int) v.getTag();
                    serviceList.remove(position);
                    break;
            }
        }
    });