Enhancing RecyclerViews: Adding Dividers in Kotlin with XML

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.