recyclerview 系统提供了默认的分割线。调用很方便。
不过,这里的系统默认分割线其实只针对类似 listview
/或者是横向listview
这种场景去使用的。
对于 gridview 模式下,使用默认的分割线就不能达到预期效果了。
针对这个问题,我参考了系统默认分割线的源码,提供了一种解决方案,感觉还是蛮好用的。
先看一下效果图:
关键代码如下:
private fun drawVertical(canvas: Canvas, parent: RecyclerView) {
canvas.save()
var left: Int
val right: Int
var itemRight = 0
if (parent.clipToPadding) {
left = parent.paddingLeft
right = parent.width - parent.paddingRight
canvas.clipRect(
left, parent.paddingTop, right,
parent.height - parent.paddingBottom
)
} else {
@Suppress("UNUSED_VALUE")
left = 0
right = parent.width
}
val childCount = parent.childCount
LogUtils.e("child count = $childCount")
for (i in 0 until childCount) {
var count = ((i + 1) % spanCount)
count = if (count == 0) spanCount else count
// LogUtils.w("$i , $count")
itemRight = count * right / spanCount
left = (count - 1) * right / spanCount
val child = parent.getChildAt(i)
parent.getDecoratedBoundsWithMargins(child, mBounds)
val bottom = mBounds.bottom + child.translationY.roundToInt()
val top = bottom - mDivider!!.intrinsicHeight
mDivider!!.setBounds(left, top, itemRight, bottom)
mDivider!!.draw(canvas)
}
canvas.restore()
}
private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
canvas.save()
var top: Int
val bottom: Int
var itemHeight = 0
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
LogUtils.e("child count = $childCount")
var line = 0
val totalLine = childCount / spanCount + if (childCount % spanCount > 0) 1 else 0
for (i in 0 until childCount) {
if ((i + 1) % (spanCount) == 1) {
line += 1
}
itemHeight = bottom * line / totalLine
top = bottom * (line - 1) / totalLine
LogUtils.w("$i , $line , $itemHeight")
val child = parent.getChildAt(i)
parent.layoutManager!!.getDecoratedBoundsWithMargins(child, mBounds)
val right = mBounds.right + child.translationX.roundToInt()
val left = right - mDivider!!.intrinsicWidth
mDivider!!.setBounds(left, top, right, itemHeight)
mDivider!!.draw(canvas)
}
canvas.restore()
}
override fun getItemOffsets(
outRect: Rect, view: View, parent: RecyclerView,
state: RecyclerView.State
) {
if (mDivider == null) {
outRect.set(0, 0, 0, 0)
return
}
when (mOrientation) {
VERTICAL -> {
outRect.set(0, 0, 0, mDivider!!.intrinsicHeight)
}
HORIZONTAL -> {
outRect.set(0, 0, mDivider!!.intrinsicWidth, 0)
}
STAGGEREDGRID -> {
outRect.set(0, 0, mDivider!!.intrinsicWidth, mDivider!!.intrinsicHeight)
}
}
}
由于主要是源码,这里只是做了少量修改,就不加注释了。简单解释一下:
首先系统方法drawVertical
和 drawHorizontal
实现是有点简单粗暴的。看里面有一个循环,根据每个 item
去画分割线,但是开始位置总是 left=0
或者top=0
,然后 right=parent.right
或者parent.bottom
。
1. 如果是列表类型的,没什么问题,这样只会导致前面的分割线被多次覆盖,不影响效果。
2. 但是,由于现在是想绘制grid
模式的,那么, right=parent.right
或者parent.bottom
会导致后面不存在的item
,也被绘制分割线了。
3. 所以,每次绘制的时候确定起始点和结束点,就可以做到grid
效果了。
使用 kotlin 去实现的,抱歉。
我理解 javaer 看 kotlin 的痛苦。