top of page

RecyclerView Item Long Click - Kotlin | Best Practice way

This article is for you if you need to execute a block of code when a user clicks and holds down on an item within your RecyclerView. I’ll assume that you already 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.


Getting Started

To correctly respond to user click and hold events, 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 long clicks. I'll call my interface RecyclerViewEvents and the function onItemLongClick.

interface RecyclerViewEvent {
    fun onItemLongClick(position: Int)
}

Suppose you followed along with the article which described how to respond to user click events. In that case, you can keep the same interface and add an additional method resulting in the following:

interface RecyclerViewEvent {
    fun onItemClick(position: Int)
    fun onItemLongClick(position: Int)
}

Don’t forget; within the function argument, we must pass a position because we would have no way of determining the clicked item. This will make more sense as we continue further.


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.OnLongClickListener”.

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

Since we’re extending "View.OnLongClickListener", 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 onLongClick and hit "OK." Android Studio will populate the class with an override to the onLongClick method, as shown below.


inner class ItemViewHolder(
    view: View
): RecyclerView.ViewHolder(view), View.OnLongClickListener {
    ...
    
    override fun onLongClick(p0: View?): Boolean {
    
    }
}

With our ViewHolder now implementing the "View.OnLongClickListener" method, we set the onLongClickListener method within the init block to either a View within a row or the entire row itself. This method is provided to us by Android, just like onClickListener. I'll attach the onLongClickListener to the whole row by passing "this" to the method, but you do whatever makes sense for your app.

inner class ItemViewHolder(
    view: View
): RecyclerView.ViewHolder(view), View.OnLongClickListener {
    ...
    
    init {
        view.setOnLongClickListener(this)
    }
    
    override fun onLongClick(p0: View?): Boolean {
    
    }
}

Finishing the onLongClick Method

To implement the onItemLongClick method defined in your Activity/Fragment interface, 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 onLongClick method. Here's all of the code that's placed within:

override fun onLongClick(p0: View?): Boolean {
    val position = adapterPosition
    if (position != RecyclerView.NO_POSITION){
        listener.onItemLongClick(position)
        return true
    }
    return false
}

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. Once the call is made, or the check fails, a Boolean is returned. The purpose of returning a Boolean is to indicate if the click-and-hold event was consumed or if it should be passed to another listener. If the position is not equal to “RecyclerView.NO_POSITION, ” notice how we return true. This indicates that the click-and-hold event was handled and should not be passed to further listeners. If the position is equal to “RecyclerView.NO_POSITION, ” we return false, indicating that the event has not been handled and should continue to any other listeners.


Wrapping Up

The adapter class is completely set up to handle click-and-hold 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 {   
    ...
}

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 onItemLongClick and hit “OK.” Android Studio will populate the class with an override to the onItemLongClick method, as shown below.

class MainActivity : AppCompatActivity(), MyAdapter.RecyclerViewEvent {   
    ...
    
    override fun onItemLongClick(position: Int) {
    
    }
}

After implementing the interface, we must pass "this" into the adapter constructor.

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 and holds down on 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 insert/remove an item. The final onItemLongClick method looks like the following:

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

    //Display the name of the clicked molecule
    Toast.makeText(
        this,
        molecule.name + ": Long Click!",
        Toast.LENGTH_SHORT
    ).show()
}

Final Result


736 views0 comments

Recent Posts

See All
bottom of page