Skip to content

Instantly share code, notes, and snippets.

@code-n-roll
Last active April 22, 2021 11:22
Show Gist options
  • Save code-n-roll/6740fcaf9155c41f6ab71668211f4842 to your computer and use it in GitHub Desktop.
Save code-n-roll/6740fcaf9155c41f6ab71668211f4842 to your computer and use it in GitHub Desktop.
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.LinearLayout
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.content.res.use
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import app.zenia.ZeniaApplication
class InnerOuterItemDecoration(
private val innerMarginInPx: Int = 0,
private val outerHorizontalMarginInPx: Int = 0,
private val outerVerticalMarginInPx: Int = 0,
@DrawableRes private val dividerDrawableId: Int? = null,
private val dividerPaddingLeftInPx: Int = 0,
private val dividerPaddingRightInPx: Int = 0,
hasDivider: Boolean = false,
private val dividerOrientation: Int = VERTICAL
) : RecyclerView.ItemDecoration() {
companion object {
private val ATTRS = intArrayOf(android.R.attr.listDivider)
private const val HORIZONTAL = LinearLayout.HORIZONTAL
private const val VERTICAL = LinearLayout.VERTICAL
}
private var divider: Drawable? = null
private val bounds = Rect()
init {
if (hasDivider) {
if (dividerDrawableId == null) {
ZeniaApplication.context.theme.obtainStyledAttributes(ATTRS).use {
divider = it.getDrawable(0)
}
} else {
divider = ContextCompat.getDrawable(ZeniaApplication.context, dividerDrawableId)
}
}
}
override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val divider = this.divider ?: return
if (parent.layoutManager == null) {
return
}
if (dividerOrientation == VERTICAL) {
drawVertical(canvas, parent, divider)
} else {
drawHorizontal(canvas, parent, divider)
}
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val currentItem = parent.getChildAdapterPosition(view)
val itemCount = parent.adapter?.itemCount ?: 0
when (val layoutManager = parent.layoutManager) {
// order is matter, because GridLayoutManager is inherited from LinearLayoutManager
is GridLayoutManager -> {
val spanCount = layoutManager.spanCount
if (layoutManager.orientation == GridLayoutManager.HORIZONTAL) {
when (currentItem) {
in 0 until spanCount -> {
outRect.left = outerHorizontalMarginInPx
}
in itemCount - spanCount until itemCount -> {
outRect.left = innerMarginInPx
outRect.right = outerHorizontalMarginInPx
}
else -> {
outRect.left = innerMarginInPx
}
}
outRect.top = outerVerticalMarginInPx
outRect.bottom = outerVerticalMarginInPx
}
}
is LinearLayoutManager -> {
if (layoutManager.orientation == LinearLayoutManager.VERTICAL) {
if (currentItem != 0) {
outRect.top = innerMarginInPx
}
outRect.left = outerHorizontalMarginInPx
outRect.right = outerHorizontalMarginInPx
if (currentItem == 0) {
outRect.top = outerVerticalMarginInPx
}
if (currentItem == itemCount - 1) {
outRect.bottom = outerVerticalMarginInPx
}
} else {
when (currentItem) {
0 -> {
outRect.left = outerHorizontalMarginInPx
}
itemCount - 1 -> {
outRect.left = innerMarginInPx
outRect.right = outerHorizontalMarginInPx
}
else -> {
outRect.left = innerMarginInPx
}
}
outRect.top = outerVerticalMarginInPx
outRect.bottom = outerVerticalMarginInPx
}
}
}
}
private fun drawVertical(canvas: Canvas, parent: RecyclerView, divider: Drawable) {
canvas.save()
val left: Int
val right: Int
if (parent.clipToPadding) {
left = parent.paddingLeft + dividerPaddingLeftInPx
right = parent.width - parent.paddingRight - dividerPaddingRightInPx
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.getDecoratedBoundsWithMargins(child, bounds)
val bottom: Int = bounds.bottom + Math.round(child.translationY)
val top: Int = bottom - divider.intrinsicHeight
divider.setBounds(left, top, right, bottom)
divider.draw(canvas)
}
canvas.restore()
}
private fun drawHorizontal(canvas: Canvas, parent: RecyclerView, divider: Drawable) {
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!!.getDecoratedBoundsWithMargins(child, bounds)
val right: Int = bounds.right + Math.round(child.translationX)
val left: Int = right - divider.intrinsicWidth
divider.setBounds(left, top, right, bottom)
divider.draw(canvas)
}
canvas.restore()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment