Set a click listener to a RecyclerView

If you’ve used a RecyclerView, you may know that they don’t have a setOnItemClickListener as ListView had, so we have to create our own way to do it. Many people asked me how to do this, so that’s why I decided to write about it.

There are many ways to achieve this, but I will show you the way I usually do it. My mechanism consists of passing the listener as a parameter to the constructor and then assign it when I bind the data to the view in onBindViewHolder.

In Java, you would need an interface that specifies listener’s behaviour. In this example, there is a sample model called ContentItem, so the click will return an item of that type:

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

In Kotlin you don’t need this. You can’t just use a lambda to get the same result. We’ll see it in a minute

If you haven’t, I encourage you to learn Kotlin, because nowadays is the present of Android! Android is Kotlin first. I recommend you to join my free masterclass so that you can easily start with it.

The constructor will receive a lambda that represents the listener, along with the items to be rendered:

class ContentAdapter(
    private val items: List<ContentItem>,
    private val listener: (ContentItem) -> Unit
) : RecyclerView.Adapter<ViewHolder>

You could alternatively create a setOnItemClickListener method and assign it that way. Now, in onBindViewHolder the view is assigned with this click listener:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val item = items[position]
    holder.bind(item)
    holder.itemView.setOnClickListener { listener(item) }
}

Use it whenever you need it by creating a new adapter and the listener that will implement the behaviour when an item is clicked. A simple example:

recycler.adapter = ContentAdapter(items) { item ->
    toast(item.title)
};

If you want to know how this toast extension function is created, go check out my article about extension functions in Kotlin.

Take a look at the whole code of this adapter. Of course there are many alternatives here. In the end, the implementation is left to the developer when using RecyclerView, so choose whatever fits better in your situation.

class ContentItem(val name: String, val imageUrl: String)

class ContentAdapter(
    private val items: List<ContentItem>,
    private val listener: (ContentItem) -> Unit
) : RecyclerView.Adapter<ContentAdapter.ViewHolder>() {

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): ViewHolder {
        val v = parent.inflate(R.layout.view_item)
        return ViewHolder(v)
    }

    override fun onBindViewHolder(
        holder: ViewHolder,
        position: Int
    ) {
        val item = items[position]
        holder.bind(item)
        holder.itemView.setOnClickListener { listener(item) }
    }
    
    override fun getItemCount(): Int {
        return items.size
    }

    class ViewHolder(itemView: View) : 
            RecyclerView.ViewHolder(itemView) {

        private val name = itemView.findViewById<TextView>(R.id.name)
        private val image = itemView.findViewById<ImageView>(R.id.image)

        fun bind(item: ContentItem) {
            name.text = item.name
            image.loadUrl(item.imageUrl)
        }

    }

}

61 thoughts on “Set a click listener to a RecyclerView”

  1. This is a great example, thanks! 😀
    Btw, I have one question. If we put object creation code inside onBindViewHolder method, is it good? Since onBindViewHolder will always be called if we scroll the content.
    I usually come up with passing the listener and list of object to ViewHolder constructor, that will be called in onCreateViewHolder. For the position, i use getAdapterPosition.
    Please advice, thank you.

    1. Yeah, it’s always better not to create objects while scrolling. I don’t think it’s a big deal these days, because listeners are very light objects, and when comparing with the amount of work the RecyclerView is doing, it won’t probably make a visible difference. But your solution is good too, of course, though you can’t return the object then, but the position. There are solutions to that with a little more code, of course.

      1. public class market_frg extends Fragment {
        private static RecyclerView.Adapter adapter;
        private RecyclerView.LayoutManager layoutManager;
        private static RecyclerView recyclerView;
        private static ArrayList data;
        public static View.OnClickListener myOnClickListener;
        private static ArrayList removedItems;

        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view=inflater.inflate(R.layout.selectable_activity,container,false);
        myOnClickListener = new MyOnClickListener(getContext());

        recyclerView = (RecyclerView)view.findViewById(R.id.my_recycler_view);
        recyclerView.setHasFixedSize(false);

        layoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

        data = new ArrayList();
        for (int i = 0; i < MyData_market.nameArray.length; i++) {
        data.add(new DataModel_market(
        MyData_market.nameArray[i],
        MyData_market.versionArray[i],
        MyData_market.id_[i],
        MyData_market.drawableArray[i]
        ));
        }
        adapter = new CustomAdapter_market(data);
        recyclerView.setAdapter(adapter);
        return view;
        }

        public static class MyOnClickListener implements View.OnClickListener {

        private final Context context;

        private MyOnClickListener(Context context) {
        this.context = context;
        }

        @Override
        public void onClick(View v) {

        }
        }

        i want here on click method,i tried but it is not working

  2. Thats a nice example, very cleanly written. I had the same concern as Elsen Novraditya but i’m glad you almost sorted it out. The only thing i’d check to see to optimize the code a bit is to see if there was a listener already bound to it. Avoids re-adding new listeners. Hence whenever onBindViewHolder() is called, it saves a little bit of time.

    I do have one request, could you please write another write up showing how it is achieved thoughtfully using data binding? I’ve come across a few ways but i’m still not entirely sure which is the “right” way

    1. I’m afraid I didn’t have time yet to start with data binding, so can’t help with that sorry.

  3. Hello Antonio, nice article
    If we have a complex itemView for each row of RecyclerView then creating a new OnClickListener in each time we want to show a row is a performance issue,(Consider a row which has different buttons and each button does something), so I came up with this solution:
    in onCreateViewHolder method after creating the ViewHolder object we set our click listeners like this : mViewHolder.mFirstBtn.setOnClickListener(this); mViewHolder.mSecondBtn.setOnClickListener(this); … and so on(its obvious that the adapter should implement View.OnClickListener interface) doing so causes each row displayed on screen (lets say 5 rows is shown at same time in the screen) be called only once! only 5 setOnClickListeners for the whole RecyclerView items without any object creation! OK,now we have to find a way to distinguish between click listeners for different positions, what we do is: in onBindViewHolder method we set the ViewHolder parameter of function as Tag of each view that we want to set click listener like this: mViewHolder.mFistBtn.setTag(mViewHolder); and so on. now when the onClick() gets called we have to determine which position was touched with this snippet: final int position = mRecyclerView.getChildAdapterPosition((ViewHolder)view.getTag.itemView);
    easy huh? looking forward for your comments, thank you

  4. Yeah, this article tries to replicate how we used `itemClickListener` in `ListView`, but for complex views it is not the best approach at all. In that case, I prefer to create a custom view, and let it deal with its own events. You can then use easier or more difficult architectural patterns to decide who is in charge of triggering the actions, but I wouldn’t go with this particular solution in every case, just when it fits.

  5. I have come to use an event bus instead of onClickListeners, it allows you to assign a private listener in onBindViewHolder that just fires an event. Then your activity/fragment handles it.

  6. how we call the ContentAdapter from MainActivity

    ContentAdapter mRecyclerAdapter = new ContentAdapter(arrayList, );

    what to send the second parameter

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

  7. Hi, thanks for this article. I did the same thing but inside the method onBindViewHolder I don’t know why it didn’t work there. I had to do it like you explayin in here, inside the ViewHolder class.

    The way I did before was passing the same value (from last item) to the listener everytime, but putting the function in view holder class worked.

    If you could shine a light on this it’d be great.

    Thank you anyway 🙂

  8. Alberto Herrera

    Hey bro! Thanks for the tutorial. I made some slight changes because I wanted a bit more customized listener but thanks overall!

  9. try like this
    holder.imageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

    Intent intent = new Intent(mContext,DetailActivity.class);
    mContext.startActivity(intent);

    }
    });

    in onBindViewHolder method.

  10. hai Antonio Leiva i want to how to create a on item click listener in fragment..please tell me..

  11. Mustafa Ogün Öztürk

    Why do we need this OnItemClickListener interface ? We can handele the click event without it.
    It is kinda confusing to me, i ll be appreciated if you can explain it. Thanks

  12. On the itel list, I can disable a button who exist in the fragment container? Can you tell me how?

  13. hello sir….how we can implement the first elemnt is enabled and others are disabled in recyclerview…

    1. You can set the listener in the onBindViewHolder(), instead of passing it to the ViewHolder for instance. In fact, that’s how I tend to do it nowadays.

    1. Hey Ajnas! Sorry, not sure what you mean. You have an example of how to implement the adapter in the article, and may use it in an activity of course.

  14. Hello Antonio Leiva, Superb tutorial, Thank you so much.
    I wanna set on click listener on card view in recycler view and play video in video view.

  15. Thanks for the great tutorial! Extremely helpful.

    I’m curious about what would make you choose one implementation over the other.

    1. It’s just a matter of trial and error. I nowadays tend to set the listener in `onBindViewHolder` instead of passing it to the `ViewHolder`, as it avoids a step. But honestly not sold to any of both solutions. Just try and stick to what works for you.

  16. I have a recycler view that contains Image and text.
    When I click image I want to go to another activity that contains more images in grid view like when I am clicked dog image it’s going to another activity that contains dogs breed image in grid view. Then I have clicked those image it shows me particular dogs breed detail.

    my English is not so good I hope you can understand.

    1. I understand yeah, but not sure what’s the question. In that “setOnClickListener” you would navigate to the specific activity you want to go after the click.

  17. thanks for the great tutorial.
    What if we want the toast to show the image title ?
    Thanks in advance

    1. If you see, in the implementation above the click returns a `ContentItem`. This is your model, so if the title of the image is there, you just need to substitute the “Item Clicked” string with the proper one.

  18. What if i want for each item to open a new activity , how do we do?
    I mean, I have a recycleview list ( sports, Food, Clothes) . .. when i click Sport => new activity opened, Food=> another activity opened..
    Can you offer an example in Kotlin?
    Thanks in advance.

    1. This example provides a general solution, you then can use it to implement what you need. You can assign an id to the ContentItem for instance, and based on that id open a specific activity.

  19. Sorry my question might seem too late or beginner like. My question is how do I implement click Listeners for the Star and the View itself.

  20. Nice work.
    Do you know how to restrict going to next activity when checkbox in recyclerview is not checked. Atleast one checkbox should be checked.

    1. It’s currently in Java yeah. I’ve been thinking about replacing the code with Kotlin and update it a little for a long time, but never find the time…

  21. Many thanks for this article. Helped me a lot though I had to struggle a bit with the declarations. In AS 3.5 for API 28 interfaces cannot be declared in a class that is not static.

    So ContentAdapter in the examble should be declared as a public static class
    That fixed everything!!!

  22. Hello, I’m facing an error “‘OnItemClickListener’ is abstract, and cannot be instantiated” when i try to use

    recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
    @Override public void onItemClick(ContentItem item) {
    Toast.makeText(getContext(), “Item Clicked”, Toast.LENGTH_LONG).show();
    }
    }));

    could you help me? thank you.

  23. Chintan Soni

    Hi sir. Thanks for the code and solution but can you give me the code pf ContentItem because while coding it is a type of one new java class to us.
    So could u help with that??

    1. That’s a sample data class. You can use your own there… But if you want something that works with this, just create a class with that name, with a public field called “name”.

Comments are closed.