Skip to content

Instantly share code, notes, and snippets.

@sergpetrov
Last active September 2, 2022 17:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sergpetrov/a04b1eccfe8f4d6b84a84b4d23036411 to your computer and use it in GitHub Desktop.
Save sergpetrov/a04b1eccfe8f4d6b84a84b4d23036411 to your computer and use it in GitHub Desktop.
import android.graphics.Canvas
import android.graphics.Rect
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
/**
* Created with Android Studio
* User: Sergey Petrov s.a.petrov.spb@gmail.com
* Date: 18/06/2018
* Time: 11:13
*
*/
class SectionItemDecoration(
parent: RecyclerView,
private val sectionCallback: Callback,
layoutResId: Int,
private val sticky: Boolean
) : RecyclerView.ItemDecoration() {
private val sectionView: View by lazy {
val view = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false)
fixLayoutSize(view, parent)
view
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
val position = parent.getChildAdapterPosition(view)
// we need add space above item if section will draw here
if (sectionCallback.isSection(position)) {
outRect.top = sectionView.height
}
}
override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(canvas, parent, state)
var previousTitle = ""
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
val position = parent.getChildAdapterPosition(child)
sectionCallback.bindData(sectionView, position)
val title = sectionCallback.getTitle(position)
if (!previousTitle.equals(title, ignoreCase = true) || sectionCallback.isSection(position)) {
drawSection(canvas, child, sectionView)
previousTitle = title
}
}
}
private fun drawSection(canvas: Canvas, child: View, sectionView: View) {
canvas.save()
if (sticky) {
canvas.translate(0f, Math.max(0, child.top - sectionView.height).toFloat())
} else {
canvas.translate(0f, (child.top - sectionView.height).toFloat())
}
sectionView.draw(canvas)
canvas.restore()
}
/**
* Measures the header view to make sure its size is greater than 0 and will be drawn
* https://yoda.entelect.co.za/view/9627/how-to-android-recyclerview-item-decorations
*/
private fun fixLayoutSize(view: View, parent: ViewGroup) {
val widthSpec = View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY)
val heightSpec = View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED)
val childWidth = ViewGroup.getChildMeasureSpec(widthSpec, parent.paddingLeft + parent.paddingRight, view.layoutParams.width)
val childHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.paddingTop + parent.paddingBottom, view.layoutParams.height)
view.measure(childWidth, childHeight)
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
}
interface Callback {
fun isSection(position: Int): Boolean
fun bindData(sectionView: View, position: Int)
fun getTitle(position: Int): String
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment