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’sremoveItem
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.