1044

I was exploring RecyclerView and I was surprised to see that RecyclerView does not have onItemClickListener().

I've two question.

Main Question

I want to know why Google removed onItemClickListener()?

Is there a performance issue or something else?

Secondary Question

I solved my problem by writing onClick in my RecyclerView.Adapter:

public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {

    public TextView txtViewTitle;
    public ImageView imgViewIcon;

    public ViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
        imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
    }

    @Override
    public void onClick(View v) {

    }
}

Is this ok / is there any better way?

Daniele Segato
  • 12,314
  • 6
  • 62
  • 88
T_V
  • 17,440
  • 6
  • 36
  • 48
  • 31
    For your code to work you need to add itemLayoutView.setOnClickListener(this); in the ViewHolder constructor. – whitneyland Jan 25 '15 at 19:53
  • 3
    you asked nice question... many developers have same doubt about RecyclerView and ListView. – venkat Aug 16 '16 at 04:29
  • 21
    **Question:** Why RecyclerView doesnot have OnItemClickListner() ? **All answers below except 2 :** How to add onclick for RecyclerView – Manohar Jan 11 '17 at 06:42
  • 4
    Wish someone actually took out the time to answer the `QUESTION` – Ojonugwa Jude Ochalifu Sep 22 '17 at 20:13
  • 1
    I think they removed it because of performance and garbage memory issues. But look on the accepted answer.... it will produce same problems. I think you made mistake by accepting it and now other ppls do mistakes because of you :). – Alex Apr 26 '18 at 08:29
  • nice explanation https://stackoverflow.com/a/31199564/5788247 – Shomu Oct 13 '18 at 11:00
  • I don't think this question should have been closed. Some answer to this question is opinion-based, included the accepted answer (which by the way doesn't answer the question). The question itself is not opinion-based. – Daniele Segato Dec 11 '19 at 14:41
  • @ojonugwaochalifu I actually answered the question. And I agree that the accepted answer shouldn't have been that one. I edited the question and vote for a reopen. Would help if OP modified the accepted answer (which I don't know how got 1200 votes). – Daniele Segato Dec 11 '19 at 15:06
  • Alex's comment to the top answer is the best solution: only need one instance of `View.OnClickHandler` that uses _View.getTag_ to retrieve the position; inside `onCreateViewHolder` call _setOnClickListener(handler)_; and inside `onBindViewHolder` call _View.setTag_ to set the position. – Sarsaparilla Dec 01 '20 at 05:30
  • You just need `bindingAdapterPosition` check in the click listener, no need for tags in a RecyclerView. You can see the official Google recommended way to implement click listener in RecyclerView in this talk: https://youtu.be/KhLVD6iiZQs?t=2048 – EpicPandaForce Jul 17 '23 at 10:55

31 Answers31

1245

tl;dr 2016 Use RxJava and a PublishSubject to expose an Observable for the clicks.

public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    String[] mDataset = { "Data", "In", "Adapter" };

    private final PublishSubject<String> onClickSubject = PublishSubject.create();

    @Override 
    public void onBindViewHolder(final ViewHolder holder, int position) {
        final String element = mDataset[position];

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               onClickSubject.onNext(element);
            }
        });
    }

    public Observable<String> getPositionClicks(){
        return onClickSubject.asObservable();
    }
}

Original Post:

Since the introduction of ListView, onItemClickListener has been problematic. The moment you have a click listener for any of the internal elements the callback would not be triggered but it wasn't notified or well documented (if at all) so there was a lot of confusion and SO questions about it.

Given that RecyclerView takes it a step further and doesn't have a concept of a row/column, but rather an arbitrarily laid out amount of children, they have delegated the onClick to each one of them, or to programmer implementation.

Think of Recyclerview not as a ListView 1:1 replacement but rather as a more flexible component for complex use cases. And as you say, your solution is what google expected of you. Now you have an adapter who can delegate onClick to an interface passed on the constructor, which is the correct pattern for both ListView and Recyclerview.

public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {

    public TextView txtViewTitle;
    public ImageView imgViewIcon;
    public IMyViewHolderClicks mListener;

    public ViewHolder(View itemLayoutView, IMyViewHolderClicks listener) {
        super(itemLayoutView);
        mListener = listener;
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
        imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
        imgViewIcon.setOnClickListener(this);
        itemLayoutView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v instanceof ImageView){
           mListener.onTomato((ImageView)v);
        } else {
           mListener.onPotato(v);
        }
    }

    public static interface IMyViewHolderClicks {
        public void onPotato(View caller);
        public void onTomato(ImageView callerImage);
    }

}

and then on your adapter

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

   String[] mDataset = { "Data" };

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

       MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() { 
           public void onPotato(View caller) { Log.d("VEGETABLES", "Poh-tah-tos"); };
           public void onTomato(ImageView callerImage) { Log.d("VEGETABLES", "To-m8-tohs"); }
        });
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager) 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) {
        // Get element from your dataset at this position 
        // Replace the contents of the view with that element 
        // Clear the ones that won't be used
        holder.txtViewTitle.setText(mDataset[position]);
    } 

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

Now look into that last piece of code: onCreateViewHolder(ViewGroup parent, int viewType) the signature already suggest different view types. For each one of them you'll require a different viewholder too, and subsequently each one of them can have a different set of clicks. Or you can just create a generic viewholder that takes any view and one onClickListener and applies accordingly. Or delegate up one level to the orchestrator so several fragments/activities have the same list with different click behaviour. Again, all flexibility is on your side.

It is a really needed component and fairly close to what our internal implementations and improvements to ListView were until now. It's good that Google finally acknowledges it.

Maragues
  • 37,861
  • 14
  • 95
  • 96
MLProgrammer-CiM
  • 17,231
  • 5
  • 42
  • 75
  • 80
    RecyclerView.ViewHolder has a getPosition() method. With some adjustments to this code, you can pass the position to the listener. – se_bastiaan Nov 05 '14 at 20:50
  • 13
    He completely missed the `onBindViewHolder(holder, position)` , which is where the info is supposed to be filled. – MLProgrammer-CiM Nov 05 '14 at 21:19
  • 1
    fantastic answer! in addition to showing the solution code-unlike most answers you find on SO for this very same question-this answer explains possible inspiration for what would otherwise seem like a nincompoop move from Google. +1 for use of a smaller class to act as the listener inteface vs passing the whole adapter to act as the interface. – KG - Jan 08 '15 at 02:45
  • Here's a neat difference I suppose if u add a onItemclick listener it seems to act in a single touch manner i.e only one item can be clicked at a time. However if you add clicklisteners to individual children views it seems to be prone to multiple simultaneous touch. – humblerookie Jan 14 '15 at 12:55
  • 26
    @se_bastiaan `getPosition()` is deprecated "This method is deprecated because its meaning is ambiguous due to the async * handling of adapter updates. Please use {@link #getLayoutPosition()} or * {@link #getAdapterPosition()} depending on your use case." – upenpat Mar 18 '15 at 12:26
  • 2
    @upenpat It might be now, it wasn't then. – se_bastiaan Mar 18 '15 at 22:38
  • 4
    @se_bastiaan yes..I meant its deprecated now ..even I used that approach and upvoted :) – upenpat Mar 19 '15 at 11:33
  • 1
    The advantage of OnItemClickListener was however that the click had an ui effect, i.e. the user sees a click animation on the view. By using the OnClickListener in the ViewHolder, this is not guaranteed any longer. A workaround is to use the following (however works only with API +11) : – JoachimR May 07 '15 at 15:58
  • It's not guaranteed because you have to define it yourself, as a lineal/relative/frame does not have a background selector by default. Put one acknowledging touches and it'll just work. – MLProgrammer-CiM May 07 '15 at 17:10
  • Excellent answer, thank you. It's working for me but I have one question. How do you know which row (position) was clicked?? I mean, you are actually clicking an item in the viewHolder, but how do you know what's the position of this viewHolder in the RecyclerView?? – Sotti May 11 '15 at 12:50
  • 8
    You have several options. Putting the position in an int field inside the viewholder, update it while binding, then returning it on click is one. – MLProgrammer-CiM May 11 '15 at 12:58
  • Out of around 10-15 answers this was the only one which helped me. I don't know if this is the best practice or not, but it did what I wanted and without bugs, where the bugs like that, edited contents of Line items replicated in non-edited/non-touched Line items. However, now I have a question. Where do you suggest to update a database table upon Line item child views like Button clicks? Less say plus/minus button are there for each row? When and where to update the db when user clicks one of them in each row in different different times? Thanks! – Randika Vishman Jun 24 '15 at 20:56
  • Wonderful, got the answer! We could do it in `onCreateViewHolder`'s our custom `ClickListner` methods implementation or at ViewHolder's overridden `OnClick` method in a `switch` with `view.getID()`! I think you may suggest the same! Thanks! – Randika Vishman Jun 24 '15 at 21:08
  • _Delegate up one level to the orchestrator so several fragments/activities have the same list with different click behaviour._ This is very useful ! – daveztong Jun 29 '15 at 10:34
  • can something like android:onClick="buttonClicked"....public void speechClicked(View v) {} be used with this approach? – user1446988 Jul 08 '15 at 18:14
  • 8
    @MLProgrammer-CiM _"You have several options. Putting the position in an int field inside the viewholder, update it while binding, then returning it on click is one."_ The ViewHolder already has this value, and is already set. Just call `viewHolder.getPosition();` – Chad Bingham Jul 23 '15 at 19:55
  • @MLProgrammer-CiM Thanks for the answer. I have learned a lot from applying and testing this. I'm using this approach and trying use it from a custom dialog, but Im stuck on something. I was wondering if you could take a look at my question..thanks [link to my question](http://stackoverflow.com/questions/31685842/custom-dialog-button-called-inside-recyclerviews-onbindviewholder) – user1446988 Jul 28 '15 at 19:59
  • Can you put any example full please ? –  Jul 31 '15 at 15:02
  • 2
    @delive Sure, write the question, put a 100 karma bounty and I'll come and answer it for you. – MLProgrammer-CiM Jul 31 '15 at 16:33
  • 2
    @MLProgrammer-CiM in another answer the view.OnClicklistener() is being set to the views, in the OnBindViewHolder(), where as you do it in the VIewHolder class. Is the other guy's way, a bad practice, because it is being assigned every time a view is being drawn. [http://stackoverflow.com/a/28304164/4127441](Here) is the answer if you didn't find my question clear. Thanks! – rgv Aug 20 '15 at 15:40
  • 2
    @Raghav either option is okay. His works better for values that change, like position or the Object associated with that position. See how my listeners only send back the view. – MLProgrammer-CiM Aug 20 '15 at 18:31
  • How can I avoid double selection (pressing two different lines almost at the same time) with this solution? – thiagolr Sep 03 '15 at 11:02
  • 1
    The main purpose of OnItemClickListener was to have access to the item / position being clicked. Moving the listener to the ViewHolder does not handle that. I think you should clarify that the viewholder has to get access to the dataset and to the currently bound position. I do think the android framework could have handled that (very common) use case. – Christophe Fondacci Sep 28 '15 at 23:57
  • 1
    The ViewHolder does not need to know about the dataset, it should only report the position touched to the adapter and let the adapter handle it. Decouples your types by inverting the dependency. – MLProgrammer-CiM Sep 29 '15 at 00:06
  • But `onItemLongClick` will be more complicated to implement in this way. – gone Nov 02 '15 at 10:39
  • @WALKER not really, you can implemente View.OnLongClickListener in your recycler view, add it to your desired views and add some methods in your inner interface (or create a second inner interface) and pass the clicks same way your are doing with regular clicks – GuillermoMP Feb 21 '16 at 09:22
  • The downsides, as I have met during development process,listeners will be a mess in `RecyclerView`, `adapters` and `viewholders`. I have encapsulated a new `ListView` that extends `RecyclerView` and implemented `onItemClick` and `onItemLongClick` functionality. This list view can fulfill many common situation. But when come to complex list item(eg. a button in list item view,and clicking the button or list item itself produces different action), it doesn't work. Anyway, after understanding the work flow, all things seems pretty convenient now. – gone Feb 22 '16 at 08:15
  • In the implementation you specify "if (v instanceof Imageview) [...]" to differentiate views from each other. But what happens, if, for example, the view holder in your list has multiple image views, and each image view has a different functionality on onClick()? How do you differentiate the views then? – Vlad Iancu May 03 '16 at 17:13
  • Add a different listener per view. – MLProgrammer-CiM May 03 '16 at 17:43
  • How can I listen to the clicks in my activity/fragment? – Sandra Jul 12 '16 at 10:32
  • 1
    Hi, great answer. Would be really nice if you could add some example code on how to get that "element" outside the adapter in te RxJava example. Thank you for your answer! :) – reixa Sep 08 '16 at 09:56
  • like gmail with long press for single and multiselection and contextual action mode for delete and share android recycelrview sample code – Harsha Oct 15 '16 at 08:20
  • no thanks am requesting sample links for if possible give code $50 per hour its too cost for any way thank you. – Harsha Oct 17 '16 at 06:40
  • am fallowing this post http://www.grokkingandroid.com/statelistdrawables-for-recyclerview-selection/ most of the stuff is implemented but only one item is selected at a time so am looking for multi selection and deselect selecteed items things – Harsha Oct 17 '16 at 06:42
  • Why setOnclickListener not call in onCreateViewHolder? – Fndroid Oct 24 '16 at 10:14
  • It's another approach, then you have to use holder.getAdapterPosition() when forwarding your component. – MLProgrammer-CiM Oct 24 '16 at 12:36
  • What I got from Google's talk was that the other approach may have race condition issues, and they went out of their way to solve those instead of promoting this one. – MLProgrammer-CiM Oct 24 '16 at 12:37
  • My `mDataset` is a `List` of objects, so I modified your code for it, except for the `public Observable getPositionClicks(){ return onClickSubject.asObservable(); }` part, how can I change it ? Changing `Observable` to `Observable`doesn't work . Thanks – Shailesh Dec 01 '16 at 13:08
  • 1
    I've been struggling with figuring out why my `TouchInterceptListeners` stopped working when I upgraded build tools to 25. The RxJava approach has helped me get rid of a whole lot of code. Thanks! – nevi_me Dec 04 '16 at 06:03
  • 1
    @Yankee try Observable> – Ujju Dec 19 '16 at 15:19
  • 1
    @MLProgrammer-CiM I think more details about implementing RxJava approach will help a lot more people, please consider adding full sample or link to refer more on this – Ujju Dec 19 '16 at 15:28
  • 3
    Adding rxJava just for that is horrible bloat and reduces maintainability. There's places RxJava is useful, but using it for click handlers without a VERY good reason is just an unmaintainable mess. – Gabe Sechan Jan 25 '17 at 18:51
  • The original solution without dependencies is right below it. The tldr is useful if you already use RxJava in your project. – MLProgrammer-CiM Jan 25 '17 at 19:58
  • I use Android Studio 2.3 with targetSdk set to 25 in Gradle. But compiler complains about onClickSubject.asObservable(); saying asObservable() cannot be resolved. Does anyone know why? I'm quite experienced in Android development (but don't call myself a specialist). – AndaluZ Mar 07 '17 at 14:26
  • 7
    Sure the *tl: w(on't)r;* is _cool_, but it's terrible _general_ advice for copy-paste SO users. Please don't add RxJava to your project just for this, and please seriously consider the overhead and maintainability burden this incurs if you don't actually know what you're doing or can't justify why you'd want RxJava to wrap input event generation. It's a pretty deep stack to dive through just for a callback. It's certainly not a _bad idea_, but please be careful. – dcow Mar 30 '17 at 19:14
  • 1
    I like your italics. – MLProgrammer-CiM Apr 01 '17 at 02:17
  • 1
    @MLProgrammer-CiM Thx for the great RxJava solution. So much cleaner/simpler. Was about to suggest an edit showing the [observer / consumer side](http://stackoverflow.com/a/44037121/2301224), but maybe too verbose / obvious. If you think it's useful, lemme know and I'll add. – Baker May 18 '17 at 07:47
  • why we should use stupid rXJava , if we want just use a stupid itemclicklistener. Who the hell create this stupid recycler view – Peter Nov 22 '17 at 15:34
  • 1
    I've nothing against using Rx, on the countrary. But creating a new `View.OnClickListener` for every bind / view you create is highly inefficient, I wish this wasn't the most voted answer / accepted answer. Teach a bad practice to users! Also, Rx is a plus to the problem of handling the click, which is Android only issue. – Daniele Segato Nov 28 '17 at 10:46
  • how to pass clicked item position?? – niranjan kurambhatti Apr 24 '18 at 07:33
  • 2
    Worst accepted answer ever on the stackoverflow for android... with so much ppls that upvote it. Highly inefficient. May be I dont understand something, who creating of onclicklistener on every single bind could be a good approach???? It will produce lot of garbage collection operations. RecyclerView was supposed to improve ListView, but such using of RecyclerView make it close to same. – Alex Apr 26 '18 at 08:22
  • Show me on this doll where this answer hurt you. &&& (+.+) ___\=/___ (|_ ~~~ _|) |___| / _ \ /_/ \_\ /_) (_\ – MLProgrammer-CiM Apr 26 '18 at 22:53
  • Now, in all seriousness. Creating 100 lightweight objects per second for a couple of seconds will not trigger GC pressure any way you look at it. Check the profiler and tell me whether I'm wrong. – MLProgrammer-CiM Apr 26 '18 at 22:54
  • If you still don't believe me, the old solution has a single click listener per view. More efficient. – MLProgrammer-CiM Apr 26 '18 at 22:58
  • Your original post was way more efficient solution. So, who you put the onClickSubject.onNext() to onBindViewHolder()?? Who not put it to the holder constructor as it was in the original post? – Alex Apr 28 '18 at 08:25
  • 1
    in case the position is the problem - you always could use view.setTag()/getTag() for example. Store the position in the tag in time of binding, then get the position from the tag in the single clicklistener for the whole adapter. – Alex Apr 28 '18 at 08:34
  • 2
    WTF you don't need PublishSubjects just to handle click events, completely unnecessary addition to this post. Although you also don't need 3 levels of interfaces either. Hrmm. – EpicPandaForce Aug 15 '18 at 23:15
  • @EpicPandaForce thank god the author decided to keep the original solution with just one interface. – MLProgrammer-CiM Aug 20 '18 at 22:11
  • 2
    @Baker, believe me, that RxJava part shouldn't be there, the original post is the way to go – Farid Nov 25 '19 at 03:45
  • @MLProgrammer-CiM The only problem of the original post is the click listener will not know for which list item the callback is for. What it will receive is the UI component that was clicked, but it's not helpful for finding the item. Actually in the onBindViewHolder() you can save the position in the holder object and let the holder be the first click listener of the contained UI components, and then let it propagate the click event to wherever needed, and when passing on the click event, the position can be carried on. – Zhou Jun 20 '20 at 23:12
135

Why the RecyclerView has no onItemClickListener

The RecyclerView is a toolbox, in contrast of the old ListView it has less build in features and more flexibility. The onItemClickListener is not the only feature being removed from ListView. But it has lot of listeners and method to extend it to your liking, it's far more powerful in the right hands ;).

In my opinion the most complex feature removed in RecyclerView is the Fast Scroll. Most of the other features can be easily re-implemented.

If you want to know what other cool features RecyclerView added read this answer to another question.

Memory efficient - drop-in solution for onItemClickListener

This solution has been proposed by Hugo Visser, an Android GDE, right after RecyclerView was released. He made a licence-free class available for you to just drop in your code and use it.

It showcase some of the versatility introduced with RecyclerView by making use of RecyclerView.OnChildAttachStateChangeListener.

Edit 2019: kotlin version by me, java one, from Hugo Visser, kept below

Kotlin / Java

Create a file values/ids.xml and put this in it:

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

then add the code below to your source

Kotlin

Usage:

recyclerView.onItemClick { recyclerView, position, v ->
    // do it
}

(it also support long item click and see below for another feature I've added).

implementation (my adaptation to Hugo Visser Java code):

typealias OnRecyclerViewItemClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Unit
typealias OnRecyclerViewItemLongClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Boolean

class ItemClickSupport private constructor(private val recyclerView: RecyclerView) {

    private var onItemClickListener: OnRecyclerViewItemClickListener? = null
    private var onItemLongClickListener: OnRecyclerViewItemLongClickListener? = null

    private val attachListener: RecyclerView.OnChildAttachStateChangeListener = object : RecyclerView.OnChildAttachStateChangeListener {
        override fun onChildViewAttachedToWindow(view: View) {
            // every time a new child view is attached add click listeners to it
            val holder = this@ItemClickSupport.recyclerView.getChildViewHolder(view)
                    .takeIf { it is ItemClickSupportViewHolder } as? ItemClickSupportViewHolder

            if (onItemClickListener != null && holder?.isClickable != false) {
                view.setOnClickListener(onClickListener)
            }
            if (onItemLongClickListener != null && holder?.isLongClickable != false) {
                view.setOnLongClickListener(onLongClickListener)
            }
        }

        override fun onChildViewDetachedFromWindow(view: View) {

        }
    }

    init {
        // the ID must be declared in XML, used to avoid
        // replacing the ItemClickSupport without removing
        // the old one from the RecyclerView
        this.recyclerView.setTag(R.id.item_click_support, this)
        this.recyclerView.addOnChildAttachStateChangeListener(attachListener)
    }

    companion object {
        fun addTo(view: RecyclerView): ItemClickSupport {
            // if there's already an ItemClickSupport attached
            // to this RecyclerView do not replace it, use it
            var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as? ItemClickSupport
            if (support == null) {
                support = ItemClickSupport(view)
            }
            return support
        }

        fun removeFrom(view: RecyclerView): ItemClickSupport? {
            val support = view.getTag(R.id.item_click_support) as? ItemClickSupport
            support?.detach(view)
            return support
        }
    }

    private val onClickListener = View.OnClickListener { v ->
        val listener = onItemClickListener ?: return@OnClickListener
        // ask the RecyclerView for the viewHolder of this view.
        // then use it to get the position for the adapter
        val holder = this.recyclerView.getChildViewHolder(v)
        listener.invoke(this.recyclerView, holder.adapterPosition, v)
    }

    private val onLongClickListener = View.OnLongClickListener { v ->
        val listener = onItemLongClickListener ?: return@OnLongClickListener false
        val holder = this.recyclerView.getChildViewHolder(v)
        return@OnLongClickListener listener.invoke(this.recyclerView, holder.adapterPosition, v)
    }

    private fun detach(view: RecyclerView) {
        view.removeOnChildAttachStateChangeListener(attachListener)
        view.setTag(R.id.item_click_support, null)
    }

    fun onItemClick(listener: OnRecyclerViewItemClickListener?): ItemClickSupport {
        onItemClickListener = listener
        return this
    }

    fun onItemLongClick(listener: OnRecyclerViewItemLongClickListener?): ItemClickSupport {
        onItemLongClickListener = listener
        return this
    }

}

/** Give click-ability and long-click-ability control to the ViewHolder */
interface ItemClickSupportViewHolder {
    val isClickable: Boolean get() = true
    val isLongClickable: Boolean get() = true
}

// Extension function
fun RecyclerView.addItemClickSupport(configuration: ItemClickSupport.() -> Unit = {}) = ItemClickSupport.addTo(this).apply(configuration)

fun RecyclerView.removeItemClickSupport() = ItemClickSupport.removeFrom(this)

fun RecyclerView.onItemClick(onClick: OnRecyclerViewItemClickListener) {
    addItemClickSupport { onItemClick(onClick) }
}
fun RecyclerView.onItemLongClick(onLongClick: OnRecyclerViewItemLongClickListener) {
    addItemClickSupport { onItemLongClick(onLongClick) }
}

(Remember you also need to add an XML file, see above this section)

Bonus feature of Kotlin version

Sometimes you do not want all the items of the RecyclerView to be clickable.

To handle this I've introduced the ItemClickSupportViewHolder interface that you can use on your ViewHolder to control which item is clickable.

Example:

class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
    override val isClickable: Boolean get() = false
    override val isLongClickable: Boolean get() = false
}

Java

Usage:

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

(it also support long item click)

Implementation (comments added by me):

public class ItemClickSupport {
    private final RecyclerView mRecyclerView;
    private OnItemClickListener mOnItemClickListener;
    private OnItemLongClickListener mOnItemLongClickListener;
    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                // ask the RecyclerView for the viewHolder of this view.
                // then use it to get the position for the adapter
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
        }
    };
    private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            if (mOnItemLongClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
            return false;
        }
    };
    private RecyclerView.OnChildAttachStateChangeListener mAttachListener
            = new RecyclerView.OnChildAttachStateChangeListener() {
        @Override
        public void onChildViewAttachedToWindow(View view) {
            // every time a new child view is attached add click listeners to it
            if (mOnItemClickListener != null) {
                view.setOnClickListener(mOnClickListener);
            }
            if (mOnItemLongClickListener != null) {
                view.setOnLongClickListener(mOnLongClickListener);
            }
        }

        @Override
        public void onChildViewDetachedFromWindow(View view) {

        }
    };

    private ItemClickSupport(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
        // the ID must be declared in XML, used to avoid
        // replacing the ItemClickSupport without removing
        // the old one from the RecyclerView
        mRecyclerView.setTag(R.id.item_click_support, this);
        mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
    }

    public static ItemClickSupport addTo(RecyclerView view) {
        // if there's already an ItemClickSupport attached
        // to this RecyclerView do not replace it, use it
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support == null) {
            support = new ItemClickSupport(view);
        }
        return support;
    }

    public static ItemClickSupport removeFrom(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support != null) {
            support.detach(view);
        }
        return support;
    }

    public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
        return this;
    }

    public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
        mOnItemLongClickListener = listener;
        return this;
    }

    private void detach(RecyclerView view) {
        view.removeOnChildAttachStateChangeListener(mAttachListener);
        view.setTag(R.id.item_click_support, null);
    }

    public interface OnItemClickListener {

        void onItemClicked(RecyclerView recyclerView, int position, View v);
    }

    public interface OnItemLongClickListener {

        boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
    }
}

How it works (why it's efficient)

This class works by attaching a RecyclerView.OnChildAttachStateChangeListener to the RecyclerView. This listener is notified every time a child is attached or detached from the RecyclerView. The code use this to append a tap/long click listener to the view. That listener ask the RecyclerView for the RecyclerView.ViewHolder which contains the position.

This is more efficient then other solutions because it avoid creating multiple listeners for each view and keep destroying and creating them while the RecyclerView is being scrolled.

You could also adapt the code to give you back the holder itself if you need more.

Final remark

Keep in mind that it's COMPLETELY fine to handle it in your adapter by setting on each view of your list a click listener, like other answer proposed.

It's just not the most efficient thing to do (you create a new listener every time you reuse a view) but it works and in most cases it's not an issue.

It is also a bit against separation of concerns cause it's not really the Job of the Adapter to delegate click events.

Daniele Segato
  • 12,314
  • 6
  • 62
  • 88
  • addOnChildAttachStateChangeListener in the constructor and never removing it? – Saba Feb 11 '20 at 18:18
  • @Saba you aren't supposed to use the constructor directly but through static methods `addTo()` and `removeFrom()`. The instance is associated to the recyclerview tag and dies with it or when it's manually removed. – Daniele Segato Mar 19 '20 at 11:48
  • So much code when all you'd need to do is set an on click listener in `onCreateViewHolder` and that's it. – EpicPandaForce Apr 26 '20 at 13:20
  • Your argument is flowed, it's not about the quantity of code but efficiency and responsibility principle and the quantity of code is not a great indication of good/bad solution, specially when that code is reusable and efficient. Once you have put that code in your project (which you can consider as a library inclusion) all you need is a 1-liner on your `RecyclerView`. – Daniele Segato Apr 27 '20 at 09:25
  • 2
    @DanieleSegato Thank you so much for this! – Mr. Octodood May 07 '20 at 18:49
  • I was confused by "(see below you also need to add an XML file)" written below the Kotlin implementation. I think this must refer to the "values/ids.xml" file shown above (near the beginning of this answer). – prepbgg Jun 19 '20 at 11:43
  • Oh! Ups, yes, i tried to keep it up to date but changing the answer i must have forgot to change that! – Daniele Segato Jul 05 '20 at 23:19
  • This is amazing. Spent hours implementing selection tracker, only to find that the selection works for both oclick and on long click (can't make it for just onclick and also vibrates for long click), and it has so many exception throwers that will crash your app for so many reasons. Selection Tracker for recycler view is USELESS. Spent more hours for looking for the best way, and this is the one! Google should most definitely put this inside RecyclerView class, and maybe throw Selection Tracker into the trash. I love this answer so much. God bless you. Thank you! – coolcool1994 Aug 25 '20 at 11:52
  • 1
    When I see words like "ItemClickSupport" it feels we took a wrong turn. I just want a mere click listener, why is it SO complicated and not obvious! – Megamozg Apr 20 '22 at 10:07
  • @daniele-segato Why `onChildViewDetachedFromWindow` method is empty? should I add this line `view.setOnClickListener(null)` ? – MML Aug 27 '22 at 00:09
  • 1
    @MML it's not needed because nothing is going to click a detached view – Daniele Segato Aug 27 '22 at 10:12
  • @Megamozg it doesn't have to be so complicated, this answer is just wrong. Refer to the official Google recommended way for implementing item click listeners instead of this one For reference, the official Google recommended way to implement "item click listeners" in RecyclerView is in this talk: https://youtu.be/KhLVD6iiZQs?t=2048, the "answer" has too much unnecessary code. TL;DR pass click listener to ViewHolder, set it there, and check for `bindingAdapterPosition` in the click listener to get the item associated with the given view. – EpicPandaForce Jul 17 '23 at 10:52
  • For this answer to be wrong it would have to not be working or have some performance issues. It is overkilling for most use cases, sure. But it isn't wrong. – Daniele Segato Jul 20 '23 at 22:58
96

I like this way and I'm using it

Inside

public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

Put

View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());

And create this class anywhere you want it

class MyOnClickListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
       int itemPosition = recyclerView.indexOfChild(v);
       Log.e("Clicked and Position is ",String.valueOf(itemPosition));
    }
}

I've read before that there is a better way but I like this way is easy and not complicated.

vishal dharankar
  • 7,536
  • 7
  • 57
  • 93
Abdulaziz Noor
  • 6,413
  • 2
  • 19
  • 21
  • 24
    should probably make `MyOnClickListener` into instance variable, because you'll be creating a new instance of it everytime with the `new` Keyword – user2968401 Apr 25 '15 at 05:18
  • 14
    recyclerView.getChildPosition(v); is depricated now use recyclerView. indexOfChild(v); – Qadir Hussain May 30 '15 at 23:26
  • 2
    This form works, but this method and implements are old ? I think.. if we can work with material desing, we need update this methods not ? –  Jul 02 '15 at 15:20
  • 4
    Don't you have issues when scrolling? On my tests the itemPosition seems to depend on views recycling, so it goes wrong compared to my full list in the adapter. – Simon Mar 01 '16 at 17:46
  • This returns 0 index in mine. Can you explain why? – The_Martian Sep 20 '16 at 07:55
  • 17
    How to get `recyclerView` in MyOnClickListener? – guo Oct 01 '16 at 02:02
  • Can you please answer this [QUESTION](http://stackoverflow.com/questions/42311592/how-can-i-locate-the-position-of-each-item-in-recyclerview-cardview?noredirect=1#comment71778010_42311592) – Arbaz Alam Feb 18 '17 at 06:39
  • This is not very efficient. It create an instance of MyOnClickListener for every view, which isn't necessary, and perform a lookup on a view to know the position while you would already have that position if using the actual callback. – Daniele Segato Nov 28 '17 at 10:43
  • 4
    Instead of using: int itemPosition = recyclerView.indexOfChild(v); you should use: RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(v); int itemPosition = holder.getAdapterPosition(); – Gordon Freeman Feb 20 '18 at 16:24
  • @guo from the view v, like: recyclerView = (RecyclerView) v.findViewById(R.id.recycler_view); – mychemicalro Oct 01 '18 at 07:27
41

Android Recyclerview With onItemClickListener, Why we cant try this is working like ListView only.

Source : Link

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

private OnItemClickListener mListener;
public interface OnItemClickListener {
    public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
    }
    return false;
}

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

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

  }
}

And Set this to RecyclerView:

    recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new            LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnItemTouchListener(
            new RecyclerItemClickListener(getActivity(), new   RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    // TODO Handle item click
                    Log.e("@@@@@",""+position);
                }
            })
    );
ColdFire
  • 6,764
  • 6
  • 35
  • 51
Guruprasad
  • 931
  • 11
  • 16
26

Thanks to @marmor, I updated my answer.

I think it's a good solution to handle the onClick() in the ViewHolder class constructor and pass it to the parent class via OnItemClickListener interface.

MyAdapter.java

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

private LayoutInflater layoutInflater;
private List<MyObject> items;
private AdapterView.OnItemClickListener onItemClickListener;

public MyAdapter(Context context, AdapterView.OnItemClickListener onItemClickListener, List<MyObject> items) {
    layoutInflater = LayoutInflater.from(context);
    this.items = items;
    this.onItemClickListener = onItemClickListener;
}

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

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    MyObject item = items.get(position);
}

public MyObject getItem(int position) {
    return items.get(position);
}


class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView title;
    private ImageView avatar;

    public ViewHolder(View itemView) {
        super(itemView);
        title = itemView.findViewById(R.id.title);
        avatar = itemView.findViewById(R.id.avatar);

        title.setOnClickListener(this);
        avatar.setOnClickListener(this);
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        //passing the clicked position to the parent class
        onItemClickListener.onItemClick(null, view, getAdapterPosition(), view.getId());
    }
}
}

Usage of adapter in other classes:

MyFragment.java

public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {

private RecyclerView recycleview;
private MyAdapter adapter;

    .
    .
    .

private void init(Context context) {
    //passing this fragment as OnItemClickListener to the adapter
    adapter = new MyAdapter(context, this, items);
    recycleview.setAdapter(adapter);
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    //you can get the clicked item from the adapter using its position
    MyObject item = adapter.getItem(position);

    //you can also find out which view was clicked
    switch (view.getId()) {
        case R.id.title:
            //title view was clicked
            break;
        case R.id.avatar:
            //avatar view was clicked
            break;
        default:
            //the whole row was clicked
    }
}

}
Shayan_Aryan
  • 2,002
  • 1
  • 29
  • 31
  • 34
    This will create and garbage-collect tons of objects when scrolling, you should instead create one OnClickListener and re-use it – marmor May 13 '15 at 11:27
  • 2
    Also `onBindViewHolder()` is called many times for the same view when scrolling. Even creating a single `OnClickListener` will not help because you will be resetting the `OnClickListener` every time `onBindViewHolder()` is called. See instead @MLProgrammer-CiM solution. – vovahost May 25 '15 at 20:32
  • You can also improve @MLProgrammer-CiM solution and instead of creating a listener object for every View in this line `MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....` you can create a single listener for all views. – vovahost May 25 '15 at 20:40
21

> How RecyclerView is different from Listview?

One difference is that there is LayoutManager class with RecyclerView by which you can manage your RecyclerView like-

Horizontal or Vertical scrolling by LinearLayoutManager

GridLayout by GridLayoutManager

Staggered GridLayout by StaggeredGridLayoutManager

Like for horizontal scrolling for RecyclerView-

LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(llm);
Ziem
  • 6,579
  • 8
  • 53
  • 86
T_V
  • 17,440
  • 6
  • 36
  • 48
21

Guys use this code in Your main activity. Very Efficient Method

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.users_list);            
UsersAdapter adapter = new UsersAdapter(users, this);
recyclerView.setAdapter(adapter);
adapter.setOnCardClickListner(this);

Here is your Adapter class.

public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UserViewHolder> {
        private ArrayList<User> mDataSet;
        OnCardClickListner onCardClickListner;


        public UsersAdapter(ArrayList<User> mDataSet) {
            this.mDataSet = mDataSet;
        }

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

        @Override
        public void onBindViewHolder(UserViewHolder holder, final int position) {
            holder.name_entry.setText(mDataSet.get(position).getUser_name());
            holder.cardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onCardClickListner.OnCardClicked(v, position);
                }
            });
        }

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

        @Override
        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
            super.onAttachedToRecyclerView(recyclerView);
        }


        public static class UserViewHolder extends RecyclerView.ViewHolder {
            CardView cardView;
            TextView name_entry;

            public UserViewHolder(View itemView) {
                super(itemView);
                cardView = (CardView) itemView.findViewById(R.id.user_layout);
                name_entry = (TextView) itemView.findViewById(R.id.name_entry);
             }
        }

        public interface OnCardClickListner {
            void OnCardClicked(View view, int position);
        }

        public void setOnCardClickListner(OnCardClickListner onCardClickListner) {
            this.onCardClickListner = onCardClickListner;
        }
    }

After this you will get this override method in your activity.

@Override
    public void OnCardClicked(View view, int position) {
        Log.d("OnClick", "Card Position" + position);
    }
waqas ali
  • 1,238
  • 1
  • 11
  • 17
  • 5
    I wonder if user scrolling up & down, OnClickListener will create & be recycle over & over again, it it efficiently? – Vinh.TV Jun 10 '16 at 02:54
  • To get the updated position of card, this minor thing can be compromised. – waqas ali Jun 10 '16 at 05:07
  • hi friend nice its working charm,i have a requirement activity with categories and products initially,if user select category icon i need to display categories in dialog box or activity if user select any category based thata load filtered data on to first activity please help me how to solve – Harsha Aug 30 '16 at 05:57
  • 1st: You need to add a method named OnProductClicked in interface OnCardClickListner like this public interface OnCardClickListner { void OnCardClicked(View view, int position); void OnProductClicked (View view, int position); } 2nd: Then implement click listener on your product icon like this. holder.yourProductIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onCardClickListner.OnProductClicked (v, position); } }); – waqas ali Sep 01 '16 at 05:48
  • I am unable to Override OnCardClicked. How are folks upvoting this answer? – The_Martian Sep 20 '16 at 07:26
  • Implement interface OnCardClickListner then you are gonna get Override OnCardClicked. – waqas ali Sep 20 '16 at 07:41
  • like gmail how to longpress on item to display contextual action mode remove and multiple options for recyclerview single and multi selection modes – Harsha Oct 15 '16 at 08:23
  • Please avoid creating an object every time you bind like proposed in this solution, it's highly inefficient, scrolling the garbage collector will have to run all the time to get rid of all those `View.OnClickListener`. – Daniele Segato Nov 28 '17 at 10:40
  • Creating a listener on every onBindViewHolder() call is NOT efficient! – Jorgesys Jul 18 '19 at 15:11
  • You should create thew click listener inside `onCreateViewHolder`, and rely on `getAdapterPosition()` to get the position. Guard against `-1`. – EpicPandaForce Apr 26 '20 at 13:25
16

RecyclerView doesn't have an onItemClickListener because RecyclerView is responsible for recycling views (surprise!), so it's the responsibility of the view that is recycled to handle the click events it receives.

This actually makes it much easier to use, especially if you had items that can be clicked in multiple places.


Anyways, detecting click on a RecyclerView item is very easy. All you need to do is define an interface (if you're not using Kotlin, in which case you just pass in a lambda):

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
    private final Clicks clicks;

    public MyAdapter(Clicks clicks) {
        this.clicks = clicks;
    }

    private List<MyObject> items = Collections.emptyList();

    public void updateData(List<MyObject> items) {
        this.items = items;
        notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations
    }

    public interface Clicks {
        void onItemSelected(MyObject myObject, int position);
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private MyObject myObject;    

        public MyViewHolder(View view) {
            super(view);
            // bind views
            view.setOnClickListener((v) -> {
                int adapterPosition = getBindingAdapterPosition();
                if(adapterPosition >= 0) {
                    clicks.onItemSelected(myObject, adapterPosition);
                }
            });
        }

        public void bind(MyObject myObject) {
            this.myObject = myObject;
            // bind data to views
        }
    }
}

Same code in Kotlin:

class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() {
    private var items: List<MyObject> = Collections.emptyList()

    fun updateData(items: List<MyObject>) {
        this.items = items
        notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations
    }

    inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) {
        private lateinit var myObject: MyObject

        init {
            // binds views
            myView.onClick {
                val adapterPosition = getBindingAdapterPosition()
                if(adapterPosition >= 0) {
                    itemClicks.invoke(myObject, adapterPosition)
                }
            }
        }

        fun bind(myObject: MyObject) {
            this.myObject = myObject
            // bind data to views
        }
    }
}

Thing you DON'T need to do:

1.) you don't need to intercept touch events manually

2.) you don't need to mess around with child attach state change listeners

3.) you don't need PublishSubject/PublishRelay from RxJava

Just use a click listener.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
15

How to put it all together example...

  • onClick() handling
  • Cursor - RecyclerView
  • ViewHolder types

    public class OrderListCursorAdapter extends CursorRecyclerViewAdapter<OrderListCursorAdapter.ViewHolder> {
    
    private static final String TAG = OrderListCursorAdapter.class.getSimpleName();
    private static final int ID_VIEW_HOLDER_ACTUAL = 0;
    private static final int ID_VIEW_HOLDER = 1;
    
    public OrderListCursorAdapter(Context context, Cursor cursor) {
        super(context, cursor);
    }
    
    public static class ViewHolderActual extends ViewHolder {
        private static final String TAG = ViewHolderActual.class.getSimpleName();
        protected IViewHolderClick listener;
        protected Button button;
    
        public ViewHolderActual(View v, IViewHolderClick listener) {
            super(v, listener);
            this.listener = listener;
            button = (Button) v.findViewById(R.id.orderList_item_button);
            button.setOnClickListener(this);
        }
    
        public void initFromData(OrderData data) {
            Log.d(TAG, "><initFromData(data=" + data + ")");
            orderId = data.getId();
            vAddressStart.setText(data.getAddressStart());
            vAddressEnd.setText(data.getAddressEnd());
        }
    
        @Override
        public void onClick(View view) {
            if (view instanceof Button) {
                listener.onButtonClick((Button) view, getPosition(), this);
            } else {
                super.onClick(view);
            }
        }
    
        public interface IViewHolderClick extends ViewHolder.IViewHolderClick {
            public void onButtonClick(Button button, int position, ViewHolder viewHolder);
        }
    }
    
    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private static final String TAG = ViewHolder.class.getSimpleName();
        protected long orderId;
        protected IViewHolderClick listener;
        protected TextView vAddressStart;
        protected TextView vAddressEnd;
        protected TextView vStatus;
    
        public ViewHolder(View v, IViewHolderClick listener) {
            super(v);
            this.listener = listener;
            v.setOnClickListener(this);
    
            vAddressStart = (TextView) v.findViewById(R.id.addressStart);
            vAddressEnd = (TextView) v.findViewById(R.id.addressEnd);
            vStatus = (TextView) v.findViewById(R.id.status);
        }
    
        public void initFromData(OrderData data) {
            Log.d(TAG, "><initFromData(data=" + data + ")");
            orderId = data.getId();
            vAddressStart.setText(data.getAddressStart());
            vAddressEnd.setText(data.getAddressEnd());
        }
    
        public long getOrderId() {
            return orderId;
        }
    
        @Override
        public void onClick(View view) {
            listener.onCardClick(view, getPosition(), this);
        }
    
        public interface IViewHolderClick {
            public void onCardClick(View view, int position, ViewHolder viewHolder);
        }
    }
    
    @Override
    public int getItemViewType(int position) {
        return position == 0 ? ID_VIEW_HOLDER_ACTUAL : ID_VIEW_HOLDER;
    }
    
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d(TAG, ">>onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")");
    
        ViewHolder result;
    
        switch (viewType) {
            case ID_VIEW_HOLDER_ACTUAL: {
                View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout_actual, parent, false);
                result = new ViewHolderActual(itemView, new ViewHolderActual.IViewHolderClick() {
                    @Override
                    public void onCardClick(View view, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
                        intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        view.getContext().startActivity(intent);
                    }
    
                    @Override
                    public void onButtonClick(Button button, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onButtonClick(button=" + button + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(button.getContext(), OrderMapActivity.class);
                        intent.putExtra(OrderMapActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        button.getContext().startActivity(intent);
                    }
                });
                break;
            }
            case ID_VIEW_HOLDER:
            default: {
                View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout, parent, false);
                result = new ViewHolder(itemView, new ViewHolder.IViewHolderClick() {
                    @Override
                    public void onCardClick(View view, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
                        intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        view.getContext().startActivity(intent);
                    }
                });
                break;
            }
        }
    
        Log.d(TAG, "<<onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")= " + result);
        return result;
    }
    
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
        Log.d(TAG, "><onBindViewHolder(viewHolder=" + viewHolder + ", cursor=" + cursor + ")");
        final OrderData orderData = new OrderData(cursor);
        viewHolder.initFromData(orderData);
    }
    }
    
Community
  • 1
  • 1
Wooff
  • 1,091
  • 12
  • 23
15

Following up MLProgrammer-CiM's excellent RxJava solution

Consume / Observe clicks

Consumer<String> mClickConsumer = new Consumer<String>() {
        @Override
        public void accept(@NonNull String element) throws Exception {
            Toast.makeText(getApplicationContext(), element +" was clicked", Toast.LENGTH_LONG).show();
        }
    };

ReactiveAdapter rxAdapter = new ReactiveAdapter();
rxAdapter.getPositionClicks().subscribe(mClickConsumer);

RxJava 2.+

Modify the original tl;dr as:

public Observable<String> getPositionClicks(){
    return onClickSubject;
}

PublishSubject#asObservable() was removed. Just return the PublishSubject which is an Observable.

Hadas Kaminsky
  • 1,285
  • 1
  • 16
  • 39
Baker
  • 24,730
  • 11
  • 100
  • 106
  • Where are you getting mClickConsumer on second line, if you please explain a little bit. rxAdapter.getPositionClicks().subscribe(mClickConsumer); – bluetoothfx Aug 18 '18 at 12:04
13

As far as I understand MLProgrammer-CiM answer, simply it's possible to just do this:

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    private ImageView image;
    private TextView title;
    private TextView price;

    public MyViewHolder(View itemView) {
        super(itemView);
        image = (ImageView)itemView.findViewById(R.id.horizontal_list_image);
        title = (TextView)itemView.findViewById(R.id.horizontal_list_title);
        price = (TextView)itemView.findViewById(R.id.horizontal_list_price);
        image.setOnClickListener(this);
        title.setOnClickListener(this);
        price.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        Toast.makeText(context, "Item click nr: "+getLayoutPosition(), Toast.LENGTH_SHORT).show();
    }
}
ZygD
  • 22,092
  • 39
  • 79
  • 102
Tomasz Mularczyk
  • 34,501
  • 19
  • 112
  • 166
10

After reading @MLProgrammer-CiM's answer, here is my code:

class NormalViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

    @Bind(R.id.card_item_normal)
    CardView cardView;

    public NormalViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
        cardView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v instanceof CardView) {
            // use getAdapterPosition() instead of getLayoutPosition()
            int itemPosition = getAdapterPosition();
            removeItem(itemPosition);
        }
    }
}
chad
  • 141
  • 1
  • 5
10

I have done this way, its very simple:

Just add 1 Line for Clicked RecyclerView position:

int position = getLayoutPosition()

Full code for ViewHolder class:

private class ChildViewHolder extends RecyclerView.ViewHolder {
        public ImageView imageView;
        public TextView txtView;

        public ChildViewHolder(View itemView) {
            super(itemView);
            imageView= (ImageView)itemView.findViewById(R.id.imageView);
            txtView= (TextView) itemView.findViewById(R.id.txtView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.i("RecyclerView Item Click Position", String.valueOf(getLayoutPosition()));
                }
            });
        }
    }

Hope this will help you.

Hiren Patel
  • 52,124
  • 21
  • 173
  • 151
8

I use this method to start an Intent from RecyclerView:

@Override
 public void onBindViewHolder(ViewHolder viewHolder, int i) {

    final MyClass myClass = mList.get(i);
    viewHolder.txtViewTitle.setText(myclass.name);
   ...
    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
      @Override
       public void onClick(View v){
             Intent detailIntent = new Intent(mContext, type.class);                                                            
             detailIntent.putExtra("MyClass", myclass);
             mContext.startActivity(detailIntent);
       }
}
);
tmac12
  • 233
  • 3
  • 11
  • It isn't good way. Because `onBindViewHolder(...)` isn't called after `notify()` methods. So, you can get wrong click position in some situations. – Aliaksei Dec 29 '17 at 20:50
5

See my approach on this:

First declare an interface like this:

/**
 * Interface used for delegating item click events in a {@link android.support.v7.widget.RecyclerView}
 * Created by Alex on 11/28/2015.
 */
  public interface OnRecyclerItemClickListener<T> {

     /**
      * Called when a click occurred inside a recyclerView item view
      * @param view that was clicked
      * @param position of the clicked view
      * @param item the concrete data that is displayed through the clicked view
      */
      void onItemClick(View view, int position, T item);
   }

Then create the adapter:

public class CustomRecyclerAdapter extends RecyclerView.Adapter {      

    private class InternalClickListener implements View.OnClickListener{

      @Override
      public void onClick(View v) {
        if(mRecyclerView != null && mItemClickListener != null){
            // find the position of the item that was clicked
            int position = mRecyclerView.getChildAdapterPosition(v);
            Data data = getItem(position);
            // notify the main listener
            mItemClickListener.onItemClick(v, position, data);
        }
    }
}

private final OnRecyclerItemClickListener mItemClickListener;
private RecyclerView mRecyclerView;    
private InternalClickListener mInternalClickListener;


/**
 *
 * @param itemClickListener used to trigger an item click event
 */
public PlayerListRecyclerAdapter(OnRecyclerItemClickListener itemClickListener){        
    mItemClickListener = itemClickListener;
    mInternalClickListener = new InternalClickListener();
}

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

    v.setOnClickListener(mInternalClickListener);

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

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // do your binding here
}

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

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);

    mRecyclerView = recyclerView;
}

@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
    super.onDetachedFromRecyclerView(recyclerView);

    mRecyclerView = null;
}

public Data getItem(int position){
    return mDataset.get(position);
}
}

And now let's see how to integrate this from a fragment:

public class TestFragment extends Fragment implements OnRecyclerItemClickListener<Data>{
   private RecyclerView mRecyclerView;

   @Override
   public void onItemClick(View view, int position, Data item) {
     // do something
   }

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      return inflater.inflate(R.layout.test_fragment, container, false);
   }

   @Override
   public void onViewCreated(View view, Bundle savedInstanceState) {
      mRecyclerView = view.findViewById(idOfTheRecycler);
      mRecyclerView .setAdapter(new CustomRecyclerAdapter(this));
   }
Alexandru Circus
  • 5,478
  • 7
  • 52
  • 89
5

If you want to add onClick() to the child view of items, for example, a button in item, I found that you can do it easily in onCreateViewHolder() of your own RecyclerView.Adapter just like this:

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v = LayoutInflater
                    .from(parent.getContext())
                    .inflate(R.layout.cell, null);

            Button btn = (Button) v.findViewById(R.id.btn);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //do it
                }
            });

            return new MyViewHolder(v);
        }

i don't know whether it's a good way, but it works well. If anyone has a better idea, very glad to tell me and correct my answer! :)

cryyyyy
  • 619
  • 1
  • 6
  • 5
5

Here is a way to implement it quite easily if you have a list of POJOs and want to retrieve one on click from outside the adapter.

In your adapter, create a listener for the click events and a method to set it:

public class MyAdapter extends RecyclerView.Adapter<SitesListAdapter.ViewHolder> {
...
private List<MyPojo> mMyPojos;
private static OnItemClickListener mOnItemClickListener;

...
public interface OnItemClickListener {
    public void onItemClick(MyPojo pojo);
}

...
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
    mOnItemClickListener = onItemClickListener;
}
...

}

In your ViewHolder, implement onClickListener and create a class member to temporarily store the POJO the view is presenting, that way (this is an example, creating a setter would be better):

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    public MyPojo mCurrentPojo;
    ...
    public ViewHolder(View view) {
        super(v);
        ...
        view.setOnClickListener(this); //You could set this on part of the layout too
    }

    ...
    @Override
    public void onClick(View view) {
        if(mOnItemClickListener != null && mCurrentPojo != null){
            mOnItemClickListener.onItemClick(mCurrentPojo);
        }
    }

Back in your adapter, set the current POJO when the ViewHolder is bound (or to null if the current view doesn't have one):

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    final MyPojo currentPojo = mMyPojos.get(position); 
    holder.mCurrentPojo = currentPojo;
    ...

That's it, now you can use it like this from your fragment/activity:

    mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(MyPojo pojo) {
            //Do whatever you want with your pojo here
        }
    });
Simon
  • 3,580
  • 2
  • 23
  • 24
  • you are trying to implement Interface as if it would be an Object method. This won't work – Akshay Nov 22 '16 at 19:38
  • Update : What you need is `mMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});` – Akshay Nov 22 '16 at 21:02
  • @Akshay Sorry but I think my code is correct. Did you try it? All the listeners on Android follow the same principle, and you need to instantiate the interface with a 'new' (ex: http://stackoverflow.com/questions/4709870/setonitemclicklistener-on-custom-listview) – Simon Nov 23 '16 at 11:52
  • Listener should not be static. – EpicPandaForce Apr 26 '20 at 13:26
4

Yes you can

public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {

    //inflate the view 

    View view = LayoutInflator.from(parent.getContext()).inflate(R.layout.layoutID,null);

    ViewHolder holder = new ViewHolder(view);

    //here we can set onClicklistener
    view.setOnClickListener(new  View.OnClickListeener(){
        public void onClick(View v)
        {
            //action
        }
    });

return holder;
Nikolay Mihaylov
  • 3,868
  • 8
  • 27
  • 32
Ashok Reddy M
  • 330
  • 3
  • 9
3

This worked for me:

@Override
public void onBindViewHolder(PlacesListViewAdapter.ViewHolder holder, int position) {
    ----
    ----
    ----
    // Set setOnClickListener(holder);
}


@Override
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    ----
    ----
    ----

    @Override
    public void onClick(View view) {
        // Use to get the item clicked getAdapterPosition()
    }
}
Naren
  • 2,706
  • 1
  • 21
  • 15
  • It isn't good way setting `setOnClickListener(holder)` in `onBindViewHolder()`. Because onBindViewHolder(...) isn't called after notify() methods. So, you can get wrong click position in some situations. – Aliaksei Dec 29 '17 at 20:53
  • You should create the click listener inside `onCreateViewHolder`. – EpicPandaForce Apr 26 '20 at 13:26
3

Here you can handle multiple onclick see below code and it is very efficient

    public class RVNewsAdapter extends RecyclerView.Adapter<RVNewsAdapter.FeedHolder> {

    private Context context;
    List<News> newsList;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    public RVNewsAdapter(List<News> newsList, Context context) {
        this.newsList = newsList;
        this.context = context;
    }

    public static class FeedHolder extends RecyclerView.ViewHolder implements OnClickListener {

        ImageView img_main;
        TextView tv_title;
        Button bt_facebook, bt_twitter, bt_share, bt_comment;


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

            img_main = (ImageView) itemView.findViewById(R.id.img_main);
            tv_title = (TextView) itemView.findViewById(R.id.tv_title);
            bt_facebook = (Button) itemView.findViewById(R.id.bt_facebook);
            bt_twitter = (Button) itemView.findViewById(R.id.bt_twitter);
            bt_share = (Button) itemView.findViewById(R.id.bt_share);
            bt_comment = (Button) itemView.findViewById(R.id.bt_comment);

            img_main.setOnClickListener(this);
            bt_facebook.setOnClickListener(this);
            bt_twitter.setOnClickListener(this);
            bt_comment.setOnClickListener(this);
            bt_share.setOnClickListener(this);

        }


        @Override
        public void onClick(View v) {

            if (v.getId() == bt_comment.getId()) {

                Toast.makeText(v.getContext(), "Comment  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_facebook.getId()) {

                Toast.makeText(v.getContext(), "Facebook  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_twitter.getId()) {

                Toast.makeText(v.getContext(), "Twitter  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_share.getId()) {

                Toast.makeText(v.getContext(), "share  " , Toast.LENGTH_SHORT).show();

            }
            else {
                Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    @Override
    public FeedHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.feed_row, parent, false);
        FeedHolder feedHolder = new FeedHolder(view);

        return feedHolder;
    }

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

        holder.tv_title.setText(newsList.get(position).getTitle());


        // Here you apply the animation when the view is bound
        setAnimation(holder.img_main, position);
    }

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


    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position) {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition) {
            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }


}
Arpit Patel
  • 7,212
  • 5
  • 56
  • 67
3

Modified my comment...

public class MyViewHolder extends RecyclerView.ViewHolder {

        private Context mContext;

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

            mContext = itemView.getContext();

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    int itemPosition = getLayoutPosition();
                    Toast.makeText(mContext, "" + itemPosition, Toast.LENGTH_SHORT).show();

                }
            });
        }
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • 1
    It's a suggestion, but better if you delete this answer, unless many people will vote negatively. This solution is definitively wrong, it works but is bad programming. Please read some blogs for RecyclerView and study some Adapter libraries, there are many valid in Github already, like FlexibleAdapter, FastAdapter and so on... – Davideas Oct 11 '16 at 23:14
  • 1
    You answer is relatively new of a long list of high voted answers and at the bottom of the page, that's why it still has 0 votes. By the way, in the new code, you should use `getAdapterPosition()`. – Davideas Oct 13 '16 at 07:52
2

Check this one in which I have implemented all the things with a proper way

RecyclerViewHolder Class

public class RecyclerViewHolder extends RecyclerView.ViewHolder  {

    //view holder is for girdview as we used in the listView
    public ImageView imageView,imageView2;
    public RecyclerViewHolder(View itemView) {
        super(itemView);
        this.imageView=(ImageView)itemView.findViewById(R.id.image);
    }

}

Adapter

public class RecyclerView_Adapter extends RecyclerView.Adapter<RecyclerViewHolder> {

    //RecyclerView will extend to recayclerview Adapter
    private ArrayList<ModelClass> arrayList;
    private Context context;
    private static RecyclerViewClickListener itemListener;
    //constructor of the RecyclerView Adapter
    RecyclerView_Adapter(Context context,ArrayList<ModelClass> arrayList,RecyclerViewClickListener itemListener){
        this.context=context;
        this.arrayList=arrayList;
        this.itemListener=itemListener;
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //this method will inflate the custom layout and return as viewHolder
        LayoutInflater layoutInflater=LayoutInflater.from(parent.getContext());
        ViewGroup mainGroup=(ViewGroup) layoutInflater.inflate(R.layout.single_item,parent,false);
        RecyclerViewHolder listHolder=new RecyclerViewHolder(mainGroup);

        return listHolder;
    }

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

        final ModelClass modelClass=arrayList.get(position);
        //holder
        RecyclerViewHolder mainHolder=(RecyclerViewHolder)holder;
        //convert the drawable image into bitmap
        Bitmap image= BitmapFactory.decodeResource(context.getResources(),modelClass.getImage());
        //set the image into imageView
        mainHolder.imageView.setImageBitmap(image);
        //to handle on click event when clicked on the recyclerview item and
        // get it through the RecyclerViewHolder class we have defined the views there
        mainHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //get the position of the image which is clicked
             itemListener.recyclerViewListClicked(v,position);
            }
        });

    }

    @Override
    public int getItemCount() {
        return (null!=arrayList?arrayList.size():0);
    }
}

The interface

public interface RecyclerViewClickListener {

    //this is method to handle the event when clicked on the image in Recyclerview
    public void recyclerViewListClicked(View v,int position);
}

//and to call this method in activity
RecyclerView_Adapter adapter=new RecyclerView_Adapter(Wallpaper.this,arrayList,this);
        recyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();


    @Override
    public void  recyclerViewListClicked(View v,int position){

        imageView.setImageResource(wallpaperImages[position]);

    }
Mudassir Khan
  • 1,714
  • 1
  • 20
  • 25
2

Access the mainView of rowLayout(cell) for you RecyclerView and in your OnBindViewHolder write this code:

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        Movie movie = moviesList.get(position);
        holder.mainView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                System.out.println("pos " + position);
            }
        });
    }
ρяσѕρєя K
  • 132,198
  • 53
  • 198
  • 213
Liridon Sadiku
  • 309
  • 3
  • 7
2

it worked for me. Hope it will help. Most simplest way.

Inside View Holder

class GeneralViewHolder extends RecyclerView.ViewHolder {
    View cachedView = null;

    public GeneralViewHolder(View itemView) {
        super(itemView);
        cachedView = itemView;
    }

Inside OnBindViewHolder()

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
            final GeneralViewHolder generalViewHolder = (GeneralViewHolder) holder;
            generalViewHolder.cachedView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(context, "item Clicked at "+position, Toast.LENGTH_SHORT).show();
                }
            });

And let me know, do you have any question about this solution ?

That's Enam
  • 286
  • 2
  • 3
  • 16
  • 1
    You should set the onClickListener in `onCreateViewHolder` for better performance, but then you need to get the position using `getAdapterPosition()` (and guard against `-1`) – EpicPandaForce Apr 26 '20 at 13:23
2
 main_recyclerview.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
        {
            int position=rv.getChildAdapterPosition(rv.findChildViewUnder(e.getX(),e.getY()));

            switch (position)
            {
                case 0:
                {
                    wifi(position);
                    adapter2.notifyDataSetChanged();
                }
                break;

                case 1:
                {
                    sound(position);
                    adapter2.notifyDataSetChanged();
                }
                break;

                case 2:
                {
                    bluetooth(position);
                    adapter2.notifyDataSetChanged();
                }
                break;


            }
            return true;
        }

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

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }
    });
dileep krishnan
  • 326
  • 4
  • 7
  • The question is _"Why doesn't RecyclerView have onItemClickListener()?"_ NOT "How to implement onItemClickListener() in RecyclerView?" . – Shashanth Oct 07 '17 at 10:51
1

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();
        }
    }
Manikanta
  • 3,421
  • 4
  • 30
  • 51
1

use PlaceHolderView

@Layout(R.layout.item_view_1)
public class View1{

    @View(R.id.txt)
    public TextView txt;

    @Resolve
    public void onResolved() {
        txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
    }

    @Click(R.id.btn)
    public void onClick(){
        txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
    }
}
Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
Janishar Ali
  • 376
  • 3
  • 8
0

I wrote a library to handle android recycler view item click event. You can find whole tutorial in https://github.com/ChathuraHettiarachchi/RecycleClick

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

or to handle item long press you can use

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
                return true;
            }
        });
0

recyclerview animation has not been tested, the other is normal. I think it has been optimized to the maximum. Interface has other uses, you can temporarily ignore.

public abstract class BaseAdapterRV<VH extends BaseViewHolder> extends RecyclerView.Adapter<VH> implements AdapterInterface {
    public final String TAG = getClass().getSimpleName();

    protected final Activity mActivity;
    protected final LayoutInflater mInflater;
    protected ItemClickInterface<?, Integer> mListener;

    public BaseAdapterRV(Activity activity) {
        mActivity = activity;
        mInflater = LayoutInflater.from(mActivity);
    }

    @Override
    public final VH onCreateViewHolder(ViewGroup parent, int viewType) {
        return onCreateViewHolder(parent, viewType, mInflater);
    }

    @Override
    public final void onBindViewHolder(VH holder, int position) {
        holder.itemView.setTag(R.id.tag_view_click, position);
        //创建点击事件
        holder.itemView.setOnClickListener(mListener);
        holder.itemView.setOnLongClickListener(mListener);
        onBindVH(holder, position);
    }


    ///////////////////////////////////////////////////////////////////////////
    // 以下是增加的方法
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 注意!涉及到notifyItemInserted刷新时立即获取position可能会不正确
     * 里面也有onItemLongClick
     */
    public void setOnItemClickListener(ItemClickInterface<?, Integer> listener) {
        mListener = listener;
        notifyDataSetChanged();
    }

    @NonNull
    protected abstract VH onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater);

    protected abstract void onBindVH(VH holder, int position);

}

This is Interface

/**
 * OnItemClickListener的接口
 * 见子类实现{@link OnItemClickListener}{@link OnItemItemClickListener}
 */
public interface ItemClickInterface<DATA1, DATA2> extends View.OnClickListener, View.OnLongClickListener {

    void onItemClick(DATA1 data1, DATA2 data2);

    boolean onItemLongClick(DATA1 data1, DATA2 data2);
}

This is an abstract class

public abstract class OnItemClickListener<DATA> implements ItemClickInterface<View, DATA> {
    @Override
    public void onClick(View v) {
        onItemClick(v, (DATA) v.getTag(R.id.tag_view_click));
    }

    @Override
    public boolean onLongClick(View v) {
        return onItemLongClick(v, (DATA) v.getTag(R.id.tag_view_click));
    }

    @Override
    public boolean onItemLongClick(View view, DATA data) {
        return false;
    }
}

You only need it

    mAdapter.setOnItemClickListener(new OnItemClickListener<Integer>() {
        @Override
        public void onItemClick(View view, Integer integer) {

        }

        @Override
        public boolean onItemLongClick(View view, Integer integer) {
            return true;
        }
    });
王 能
  • 761
  • 5
  • 4
0

I found one of the shortest ways using androidx lifecycle mutable live data

Adapter:

private val onItemClickListener = MutableLiveData<YourAdapterItem>()


override fun onBindViewHolder(holder: GifsViewHolder, position: Int) {
    holder.itemView.setOnClickListener { onItemClickListener.value = gifs[position] }
}
fun getOnItemClickListener(): MutableLiveData<Gif> {
    return onItemClickListener
}

anywhere in MainActivity

    yourFancyAdapter.getOnItemClickListener().observe(this, Observer {
        println(it)
    })
sanevys
  • 559
  • 1
  • 7
  • 27
-5

Easiest way to do this is as follows:

Declare global variable at start of Adapter class:

// Store out here so we can resuse
private View yourItemView;

Then set the OnClickListener within the onBindViewHolder method:

@Override
public void onBindViewHolder(BusinessAdapter.ViewHolder holder, int position) {

    // Set up the on click listener
    yourItemView.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            Toast.makeText(mContext,Integer.toString(position),Toast.LENGTH_SHORT).show();
        }
    });

}

All other answers are incorrect.

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
dave
  • 1
  • 1
  • how do you get the position in OnClick? – batsheva Jul 27 '17 at 05:20
  • You can attach position in tag. yourItemView.setTag(position) – wdina Aug 07 '17 at 07:31
  • It should be done in onCreateViewHolder method and not onBindViewHolder method. Also, no such comments ('All other answers are lame') on other answers – Embydextrous Aug 31 '17 at 09:05
  • @batsheva - it's already in the onBindViewHolder method. Updated solution. – dave Oct 14 '17 at 00:30
  • @Embydextrous - why should it be done in onCreateViewHolder? All other answers are lame. None of them address the question and the ones that do require lines and lines of code to do something simple. – dave Oct 14 '17 at 00:33
  • `how do you get the position in OnClick?` you use `getAdapterPosition()` – EpicPandaForce Aug 15 '18 at 23:19