top of page

RecyclerView Item Click - Kotlin

You'll often need to display more information about each item in your RecyclerView. One common practice is creating a detail page that shows more information about the clicked item. In this article, we'll write some code to respond to item clicks. I'll assume you have a functioning RecyclerView created in your application. If you're looking to set one up, take a look at this article, where I'll take you through all the steps required.


YouTube Video


GitHub


Getting Started

To correctly respond to user clicks, we utilize an interface to communicate between the adapter class and the Activity/Fragment. One common mistake is writing the item click logic within the adapter. The adapter is solely responsible for putting data into the RecyclerView; writing logic to navigate to a different screen would be an anti-pattern. With that said, go to your adapter class and create an interface with a function to handle item clicks. I'll call my interface RecyclerViewEvents and the function onItemClick.

interface RecyclerViewEvent {
    fun onItemClick(position: Int)
}

Within the function argument, we must pass a position because we would have no way of determining the clicked item. You'll see what I mean later in the article.

Modifying the ViewHolder Inner Class

With the recently created interface, we now have a function that can send the position of the clicked item to the Activity/Fragment displaying the RecyclerView. We have to call this function every time the user clicks an item. To do this, the ViewHolder inner class must extend View.OnClickListener.

inner class ItemViewHolder(
    view: View
) : RecyclerView.ViewHolder(view), View.OnClickListener {

    //Getting reference to views within the row layout
    ...
}

Since we’re extending "View.OnClickListener", we need to implement the interface members. You should see that the inner class is underlined in red. Hover over that red line, or click the red lightbulb and select “implement members.” A dialog will appear; choose onClick and hit “OK.” Android Studio will populate the class with an override to the onClick method, as shown below.


inner class ItemViewHolder(
    view: View
) : RecyclerView.ViewHolder(view), View.OnClickListener {

    //Getting reference to views within the row layout
    ...
    
    override fun onClick(p0: View?) {
        TODO("Not yet implemented")
    }
}

With our ViewHolder implementing the "View.OnClickListener" method, we now set an onClickListener within the init block to either a View within a row or the entire row itself. I'll attach the onClickListener to the whole row, but you do whatever makes sense for your app.

inner class ItemViewHolder(
    view: View
) : RecyclerView.ViewHolder(view), View.OnClickListener {

    //Getting reference to views within the row layout
    ...
    
    init {
        view.setOnClickListener(this)
    }

    override fun onClick(p0: View?) {
        TODO("Not yet implemented")
    }
}

We can pass "this" into the setOnClickListener method because the inner class implements the "View.OnClickListener" interface.

Finishing the onClick Method

To call the onItemClick method on your Activity/Fragment, we’ll need to have a reference to either within the adapter. Go to the top of your file, and in the RecyclerView adapter constructor, add a new private value with the type as your interface. Here, I call it “listener” and specify the type as “RecyclerViewEvent.”

class MyAdapter(
    private val data: List<Molecule>,
    private val listener: RecyclerViewEvent
) : RecyclerView.Adapter<MyAdapter.ItemViewHolder>() {

    ...
}

With this, we can now finish the overridden onClick method. Here's all of the code that's placed within:

override fun onClick(p0: View?) {
    val position = adapterPosition
    if (position != RecyclerView.NO_POSITION){
        listener.onItemClick(position)
    }
}

To get the position of the clicked item, we call "adapterPosition" which returns the position of the item represented by the ViewHolder. We then check if the position equals "RecyclerView.NO_POSITION", which is just an integer constant of -1. The reason for this check is to account for the case where you call "notifyDataSetChanged." In the case of an item removal, if the user clicks the removed item while the animation is still running, that item will have a position of -1. We do not want to pass that into our interface method because if you try and get data out of a list of items, it'll result in an index error. If the "RecyclerView.NO_POSITION" check passes, we make the call to the interface method passing in the position of the clicked item.


Wrapping Up

The adapter class is completely set up to handle click events. Now, you must make your way to the Activity/Fragment, where you set the adapter to your RecyclerView. You should see that the adapter is underlined in red. The reason for this is the extra parameter we added to the constructor. Before passing a value to the constructor, you must first implement the interface we created in the adapter class.

class MainActivity : AppCompatActivity(), MyAdapter.RecyclerViewEvent {
    private val data = createData()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView: RecyclerView =        
            findViewById(R.id.theBestRecyclerViewOnThePlanet)
        recyclerView.adapter = MyAdapter(data)

        recyclerView.layoutManager = LinearLayoutManager(this)
    }
    
    ...
}

The Activity/Fragment should be underlined in red after implementing the interface. Hover over that red line, or click the red lightbulb and select “implement members.” A dialog will appear; choose onItemClick and hit “OK.” Android Studio will populate the class with an override to the onItemClick method, as shown below.

class MainActivity : AppCompatActivity(), MyAdapter.RecyclerViewEvent {
    private val data = createData()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView: RecyclerView =        
            findViewById(R.id.theBestRecyclerViewOnThePlanet)
        recyclerView.adapter = MyAdapter(data, this)

        recyclerView.layoutManager = LinearLayoutManager(this)
    }
    
    override fun onItemClick(position: Int) {
        TODO("Not yet implemented")
    }

    ...
}

After implementing the interface, we must pass "this" into the adapter constructor because the Activity implements the interface defined in the adapter class.

recyclerView.adapter = MyAdapter(createData(), this)

If you’re wondering what “createData()” is, it’s a method I wrote in the previous article that returns a list of items serving as the data for the adapter.


The last thing you need to do is write the code that should run when a user clicks an item in the RecyclerView. I will display a toast message for this article to show that everything works. Feel free to navigate to a new Activity/Fragment or implement a detail view. The final onItemClick method looks like the following:

override fun onItemClick(position: Int) {
    val molecule = data[position]

    //Display the name of the clicked molecule
    Toast.makeText(
        this,
        molecule.name,
        Toast.LENGTH_SHORT
    ).show()
}

Final Result



1,864 views0 comments

Recent Posts

See All
bottom of page