很多时候我们在用RecyclerView时需要用到分割线。有时候图省事就用了一个View布局给他设置个背景色来实现,这样做虽然可以实现分割线的效果,但是这么做是不是有点low了。。。。
其实RecyclerView自己就有设置分割线的方法addItemDecoration,通过这个方法我们可以很方便的设置分割线。同时系统还为我们提供了一个分割线的类DividerItemDecoration。
今天我们就来自定义一个分割线的类VHDividerItemDecoration。首先我们自定义分割线需要实现一个类ItemDecoration。
public abstract static class ItemDecoration {
/**
* Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
* Any content drawn by this method will be drawn before the item views are drawn,
* and will thus appear underneath the views.
*
* @param c Canvas to draw into
* @param parent RecyclerView this ItemDecoration is drawing into
* @param state The current state of RecyclerView
*/
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
/**
* @deprecated
* Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
*/
@Deprecated
public void onDraw(Canvas c, RecyclerView parent) {
}
/**
* Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
* Any content drawn by this method will be drawn after the item views are drawn
* and will thus appear over the views.
*
* @param c Canvas to draw into
* @param parent RecyclerView this ItemDecoration is drawing into
* @param state The current state of RecyclerView.
*/
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
/**
* @deprecated
* Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
*/
@Deprecated
public void onDrawOver(Canvas c, RecyclerView parent) {
}
/**
* @deprecated
* Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
*/
@Deprecated
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
outRect.set(0, 0, 0, 0);
}
/**
* Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
* the number of pixels that the item view should be inset by, similar to padding or margin.
* The default implementation sets the bounds of outRect to 0 and returns.
*
* <p>
* If this ItemDecoration does not affect the positioning of item views, it should set
* all four fields of <code>outRect</code> (left, top, right, bottom) to zero
* before returning.
*
* <p>
* If you need to access Adapter for additional data, you can call
* {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
* View.
*
* @param outRect Rect to receive the output.
* @param view The child view to decorate
* @param parent RecyclerView this ItemDecoration is decorating
* @param state The current state of RecyclerView.
*/
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
}
这里有6个方法,其中前4个是画分割线,后2个是设置偏移量.我们只需要重写其中的两个
fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State?)
fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView,
state: RecyclerView.State?)
这里我的思路是,定义一个画分割线的类Decoration,在其中定义x,y方向上的偏移量,和颜色,然后重写getItemOffsets方法,通过Decoration得到偏移量,然后再通过Ondraw()方法画出来。
具体实现方法如下
package net.ishandian.app.shop.weight
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.support.annotation.ColorInt
import android.support.v7.widget.RecyclerView
import android.view.View
/**
* ================================================
* @author:黄佃华
* @date:2018/7/26 16:36.
* @description:RecyclerView分割线,3种(水平|垂直|水平和垂直)
* ================================================
*/
abstract class VHDividerItemDecoration() : RecyclerView.ItemDecoration() {
companion object {
const val HORIZONTAL = 0
const val VERTICAL = 1
const val VH = 2
}
/**
* Current orientation. Either [.HORIZONTAL] or [.VERTICAL] or [.VH].
*/
private var mOrientation: Int = 0
private val mBounds = Rect()
constructor(orientation: Int) : this() {
setOrientation(orientation)
}
/**
* Sets the orientation for this divider. This should be called if
* [RecyclerView.LayoutManager] changes orientation.
*
* @param orientation [.HORIZONTAL] or [.VERTICAL] or [.VH]
*/
fun setOrientation(orientation: Int) {
if (orientation != HORIZONTAL && orientation != VERTICAL && orientation != VH) {
throw IllegalArgumentException(
"Invalid orientation. It should be either HORIZONTAL or VERTICAL")
}
mOrientation = orientation
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State?) {
if (parent.layoutManager == null) {
return
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent)
} else if (mOrientation == HORIZONTAL) {
drawHorizontal(c, parent)
} else {
drawVH(c, 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
var decoration = getItemDecoration()
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
parent.getDecoratedBoundsWithMargins(child, mBounds)
val bottom = mBounds.bottom + Math.round(child.translationY)
val top = bottom - decoration.height
decoration.drawDecorate(canvas, left, top, right, bottom)
}
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
var decoration = getItemDecoration()
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
parent.layoutManager.getDecoratedBoundsWithMargins(child, mBounds)
val right = mBounds.right + Math.round(child.translationX)
val left = right - decoration.width
decoration.drawDecorate(canvas, left, top, right, bottom)
}
canvas.restore()
}
private fun drawVH(canvas: Canvas, parent: RecyclerView) {
canvas.save()
val top: Int
val bottom: Int
val left: Int
val right: Int
if (parent.clipToPadding) {
left = parent.paddingLeft
right = parent.width - parent.paddingRight
top = parent.paddingTop
bottom = parent.height - parent.paddingBottom
} else {
left = 0
top = 0
bottom = parent.height
right = parent.width
}
val childCount = parent.childCount
var decoration = getItemDecoration()
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
parent.getDecoratedBoundsWithMargins(child, mBounds)
//下面的线
val bottomB = mBounds.bottom + Math.round(child.translationY)
val topB = bottomB - decoration.height
decoration.drawDecorate(canvas, left, topB, right, bottomB)
//上面的线
val topT = mBounds.top + Math.round(child.translationY)
val bottomT = topT + decoration.height
decoration.drawDecorate(canvas, left, topT, right, bottomT)
//右边的线
val rightR = mBounds.right + Math.round(child.translationX)
val leftR = rightR - decoration.width
decoration.drawDecorate(canvas, leftR, top, rightR, bottom)
//左边的线
val leftL = mBounds.left + Math.round(child.translationX)
val rightL = leftL + decoration.width
decoration.drawDecorate(canvas, leftL, top, rightL, bottom)
}
canvas.restore()
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView,
state: RecyclerView.State?) {
var decoration = getItemDecoration()
if (decoration == null) {
outRect.set(0, 0, 0, 0)
return
}
if (mOrientation == VERTICAL) {
outRect.set(0, 0, 0, decoration.height)
} else if (mOrientation == HORIZONTAL) {
outRect.set(0, 0, decoration.width, 0)
} else {
outRect.set(0, 0, decoration.width, decoration.height)
}
}
abstract fun getItemDecoration(): Decoration
class Decoration(val width: Int, val height: Int, @ColorInt val color: Int = Color.GRAY) {
private var paint = Paint()
fun drawDecorate(canvas: Canvas, left: Int, top: Int, right: Int, bottom: Int) {
paint.color = color
canvas.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), paint)
}
}
}