In Android development, the RecyclerView
is a versatile widget used for displaying large datasets efficiently. One common requirement is to add visual separators, such as dividers, between the items in a RecyclerView
. This enhances readability and the overall user experience. In this blog post, we’ll explore how to add item decorations, specifically dividers, to a RecyclerView
in Kotlin using XML for layout definitions.
What is an Item Decoration?
An ItemDecoration
is a class in Android that allows you to add visual elements (like dividers) and modify the appearance of items in a RecyclerView
. You can create custom item decorations or use predefined ones like DividerItemDecoration
.
Why Use Item Decorations?
- Enhanced Readability: Dividers make it easier to distinguish between items.
- Improved Aesthetics: Decorations can improve the visual appeal of your app.
- Reusability: Decorations can be reused across multiple
RecyclerView
instances.
How to Add Dividers to a RecyclerView
in Kotlin with XML
Here’s a step-by-step guide to adding dividers to a RecyclerView
:
Step 1: Add the RecyclerView
Dependency
First, ensure that your project includes the necessary RecyclerView
dependency. Open your build.gradle
file and add the following:
dependencies {
implementation("androidx.recyclerview:recyclerview:1.3.2")
// If you need the selectable features
implementation("androidx.recyclerview:recyclerview-selection:1.1.0")
}
Step 2: Define the RecyclerView
in XML
In your layout XML file (e.g., activity_main.xml
), add the RecyclerView
widget:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"/>
Step 3: Create an Item Decoration Class
Create a Kotlin class to define the item decoration. We’ll use DividerItemDecoration
, but you can also create a custom decoration if needed.
import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
class DividerItemDecoration(context: Context, orientation: Int) : RecyclerView.ItemDecoration() {
private var divider: Drawable?
private var orientation: Int = 0
init {
val styledAttributes = context.obtainStyledAttributes(ATTRS)
divider = styledAttributes.getDrawable(0)
styledAttributes.recycle()
this.orientation = orientation
}
companion object {
private val ATTRS = intArrayOf(android.R.attr.listDivider)
const val HORIZONTAL = 0
const val VERTICAL = 1
}
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
if (parent.layoutManager == null || divider == null) {
return
}
if (orientation == VERTICAL) {
drawVertical(canvas, parent)
} else {
drawHorizontal(canvas, parent)
}
}
private fun drawVertical(canvas: Canvas, parent: RecyclerView) {
canvas.save()
val left: Int
val right: Int
if (parent.clipToPadding) {
left = parent.paddingLeft
right = parent.width - parent.paddingRight
canvas.clipRect(
left, parent.paddingTop, right,
parent.height - parent.paddingBottom
)
} else {
left = 0
right = parent.width
}
val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
parent.layoutManager?.getDecoratedBottom(child)?.let {
val top: Int = it + Math.round(child.translationY)
val bottom: Int = top + (divider?.intrinsicHeight ?: 0)
divider?.setBounds(left, top, right, bottom)
divider?.draw(canvas)
}
}
canvas.restore()
}
private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
canvas.save()
val top: Int
val bottom: Int
if (parent.clipToPadding) {
top = parent.paddingTop
bottom = parent.height - parent.paddingBottom
canvas.clipRect(
parent.paddingLeft, top,
parent.width - parent.paddingRight, bottom
)
} else {
top = 0
bottom = parent.height
}
val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
parent.layoutManager?.getDecoratedRight(child)?.let {
val left: Int = it + Math.round(child.translationX)
val right: Int = left + (divider?.intrinsicWidth ?: 0)
divider?.setBounds(left, top, right, bottom)
divider?.draw(canvas)
}
}
canvas.restore()
}
}
And you would also have to ensure that the orientation can be applied and that you can obtain your own divider resource or let the system have one, e.g:
init {
divider = ContextCompat.getDrawable(context, R.drawable.my_divider)
this.orientation = orientation
}
Step 4: Configure the RecyclerView
in the Activity or Fragment
In your Activity or Fragment, get a reference to the RecyclerView
and set its layout manager and item decoration:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
// Create a list of data for the RecyclerView
val items = listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
// Set the adapter
val adapter = MyAdapter(items)
recyclerView.adapter = adapter
// Add the divider item decoration
val dividerItemDecoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
recyclerView.addItemDecoration(dividerItemDecoration)
}
}
Step 5: Create the Adapter
Here is an example of a simple Adapter class for demonstration purposes:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class MyAdapter(private val items: List) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
}
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.textView.text = items[position]
}
override fun getItemCount(): Int {
return items.size
}
}
Step 6: Define the Item Layout (item_layout.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textSize="18sp" />
</LinearLayout>
Customizing the Divider
You can customize the divider by:
- Using a custom drawable: Override the divider property in the
DividerItemDecoration
class to use your own drawable resource. - Adjusting padding: Modify the bounds of the divider in the
onDraw
method to control its position.
Conclusion
Adding item decorations, such as dividers, to a RecyclerView
in Kotlin enhances the user experience by improving the visual structure of your app. Using the DividerItemDecoration
class or creating a custom implementation provides flexibility in how you present lists of data. With these techniques, you can make your Android apps more readable and visually appealing.