用Kotlin寫一個RecyclerView的自定義分割線
阿新 • • 發佈:2019-01-04
很多時候我們在用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) } } }