Search code examples
androidkotlinandroid-recyclerviewandroid-drawabledivider

Add Divider Top,Middle and Bottom of Recyclerview Kotlin


Hey I want to show divider in top, middle and bottom in Recyclerview. How to add dividers and spaces between items in RecyclerView. It work to add divider in middle and bottom. But I cannot find to add divider on top of first item. Has anyone know how to achieve this? Thanks in advance.

import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.view.View
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.letsgetchecked.app.R


class SimpleDividerItemDecoration(private val context: Context) : RecyclerView.ItemDecoration() {

    private var drawable: Drawable? = ContextCompat.getDrawable(context, R.drawable.cloudy)

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val left: Int = parent.paddingLeft
        val right: Int = parent.width - parent.paddingRight

        val childCount: Int = parent.childCount
        for (i in 0 until childCount) {
            val child: View = parent.getChildAt(i)
            val params = child.layoutParams as RecyclerView.LayoutParams
            val top: Int = child.bottom + params.bottomMargin
            val bottom: Int = top + drawable?.intrinsicHeight!!
            drawable?.setBounds(left, top, right, bottom)
            drawable?.draw(c)
        }
    }
}

Expected Output

enter image description here

What I am getting

enter image description here


Solution

  • You don't need to write your own class. The way you're doing it does not account for the thickness of the divider, so it could lead to inaccurate padding in your list items. Use the provided class DividerItemDecoration.

    However, it is also missing a divider above the top item. Here's a class you can add as a second decoration. It draws a divider over only the first item, so you can customize it to look different from the rest, which I think is a good idea for usability reasons. I based it off the source of DividerItemDecoration so it properly accounts for padding and clipping.

    class TopDividerItemDecoration(val context: Context) : RecyclerView.ItemDecoration() {
        private val bounds = Rect()
        private var _drawable: Drawable? =
            context.obtainStyledAttributes(intArrayOf(android.R.attr.listDivider)).use {
                it.getDrawable(0)
            }
        var drawable: Drawable
            get() = _drawable
                ?: error("A drawable must be set before use. Current theme lacks default divider.")
            set(value) {
                _drawable = value
            }
    
        override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
            if (parent.layoutManager == null || parent.childCount == 0)
                return
    
            c.save()
            if (parent.clipToPadding) {
                c.clipRect(
                    parent.paddingLeft, parent.paddingTop, parent.width - parent.paddingRight,
                    parent.height - parent.paddingBottom
                )
            }
    
            val child = parent.getChildAt(0)
            parent.getDecoratedBoundsWithMargins(child, bounds)
            val top = bounds.top + child.translationY.roundToInt()
            val bottom = top + drawable.intrinsicHeight
            drawable.setBounds(0, top, parent.width, bottom)
            drawable.draw(c)
    
            c.restore()
        }
    
        override fun getItemOffsets(
            outRect: Rect,
            view: View,
            parent: RecyclerView,
            state: RecyclerView.State
        ) {
            super.getItemOffsets(outRect, view, parent, state)
            if (parent.getChildLayoutPosition(view) == 0) {
                outRect.top += drawable.intrinsicHeight
            }
        }
    }