用Kotlin写一个RecyclerView的自定义分割线

很多时候我们在用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)
        }
    }
}

猜你喜欢

转载自blog.csdn.net/android_hdh/article/details/81236608