Swipe-to-Dismiss in RecyclerView with Kotlin: A Practical Guide

The swipe-to-dismiss functionality is a common and intuitive UI pattern in modern mobile applications. It allows users to remove or archive items from a list by swiping them left or right. Implementing this feature in a RecyclerView can significantly improve user experience. This blog post delves into how to implement swipe-to-dismiss in a RecyclerView using Kotlin within the traditional XML layout environment.

What is Swipe-to-Dismiss?

Swipe-to-dismiss is a UI interaction where users can remove an item from a list or perform an action on that item by swiping it horizontally. This pattern is frequently used in email apps, task managers, and other list-based applications.

Why Implement Swipe-to-Dismiss?

  • Improved User Experience: Provides an intuitive way for users to manage items.
  • Enhanced Interactivity: Makes the application feel more responsive and interactive.
  • Efficiency: Allows users to quickly remove or archive items from a list.

Prerequisites

Before we start, make sure you have the following:

  • Android Studio installed.
  • Basic knowledge of Kotlin and Android development.
  • Familiarity with RecyclerView and Adapters.

Steps to Implement Swipe-to-Dismiss in RecyclerView

Implementing swipe-to-dismiss in a RecyclerView involves a few key steps. We will cover each step in detail, including the code snippets and explanations.

Step 1: Set Up Your Project

Create a new Android project or open an existing one in Android Studio.

Step 2: Add the RecyclerView Dependency

Make sure you have the RecyclerView dependency in your build.gradle file:

dependencies {
    implementation("androidx.recyclerview:recyclerview:1.3.2")
    implementation("androidx.recyclerview:recyclerview-selection:1.1.0")
}

Sync your project after adding the dependency.

Step 3: Create the RecyclerView Layout

Create an XML layout file for your RecyclerView:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 4: Create the RecyclerView Item Layout

Create a layout file for each item in the RecyclerView. This example uses a simple TextView:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="4dp"
    app:cardCornerRadius="4dp"
    app:cardElevation="2dp">

    <TextView
        android:id="@+id/textViewItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:textSize="16sp" />

</androidx.cardview.widget.CardView>

Step 5: Create the RecyclerView Adapter

Implement the RecyclerView adapter in Kotlin:

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.cardview.widget.CardView

class ItemAdapter(private val itemList: MutableList<String>) :
    RecyclerView.Adapter<ItemAdapter.ViewHolder>() {

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textViewItem: TextView = itemView.findViewById(R.id.textViewItem)
        val cardView: CardView = itemView.findViewById(R.id.cardView)
    }

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.textViewItem.text = itemList[position]
    }

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

    fun removeItem(position: Int) {
        itemList.removeAt(position)
        notifyItemRemoved(position)
    }
}

Step 6: Implement the ItemTouchHelper

ItemTouchHelper is a utility class for adding swipe-to-dismiss and drag-and-drop support to RecyclerViews. Create an ItemTouchHelper callback:

import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class SwipeToDeleteCallback(private val adapter: ItemAdapter) :
    ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false // We don't support drag & drop
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        val position = viewHolder.adapterPosition
        adapter.removeItem(position)
    }
}

Here’s what this class does:

  • Extends ItemTouchHelper.SimpleCallback to provide basic swipe functionality.
  • Overrides onMove to disable drag-and-drop support.
  • Overrides onSwiped to handle the swipe action. When an item is swiped, the adapter’s removeItem method is called to remove the item from the list.

Step 7: Integrate ItemTouchHelper with RecyclerView in Activity

In your Activity or Fragment, attach the ItemTouchHelper to the RecyclerView:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.ItemTouchHelper

class MainActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var itemAdapter: ItemAdapter
    private val itemList = mutableListOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")

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

        recyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)

        itemAdapter = ItemAdapter(itemList)
        recyclerView.adapter = itemAdapter

        val swipeToDeleteCallback = SwipeToDeleteCallback(itemAdapter)
        val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }
}

Step 8: Add Visual Feedback (Optional)

To enhance the user experience, you can add visual feedback when an item is swiped. You can achieve this by implementing the onChildDraw method in the ItemTouchHelper.SimpleCallback.

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class SwipeToDeleteCallback(private val adapter: ItemAdapter) :
    ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {

    private val background = ColorDrawable(Color.RED)

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        val position = viewHolder.adapterPosition
        adapter.removeItem(position)
    }

    override fun onChildDraw(
        c: Canvas,
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        dX: Float,
        dY: Float,
        actionState: Int,
        isCurrentlyActive: Boolean
    ) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)

        val itemView = viewHolder.itemView
        val backgroundCornerOffset = 20 //so we are seeing background

        if (dX > 0) {
            background.setBounds(itemView.left, itemView.top, itemView.left + dX.toInt() + backgroundCornerOffset, itemView.bottom)
        } else if (dX < 0) {
            background.setBounds(itemView.right + dX.toInt() - backgroundCornerOffset, itemView.top, itemView.right, itemView.bottom)
        } else {
            background.setBounds(0, 0, 0, 0)
        }

        background.draw(c)
    }
}

Complete Code Example

Here is the complete code for MainActivity.kt:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.ItemTouchHelper

class MainActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var itemAdapter: ItemAdapter
    private val itemList = mutableListOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")

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

        recyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)

        itemAdapter = ItemAdapter(itemList)
        recyclerView.adapter = itemAdapter

        val swipeToDeleteCallback = SwipeToDeleteCallback(itemAdapter)
        val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }
}

Conclusion

Implementing swipe-to-dismiss in a RecyclerView in Kotlin using XML layouts enhances the interactivity and user experience of your Android applications. By following the steps outlined in this guide, you can easily integrate this feature into your projects, making your apps more user-friendly and efficient. The addition of visual feedback further enhances the user experience, providing clear cues when an item is swiped. Embrace this pattern to create modern and engaging Android applications.