This is the 5th day of my participation in the August More Text Challenge

The official website explains: An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter’s data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.

ItemDecoration allows us to giverecyclerviewIn theitemAdd specialized drawing and layout; Such as dividing lines, emphasis and decoration.

The default recyclerView behaves like this

Actually, this is what I wanted

If we don’t use this, then we can only add values like layout_margin when writing XML files, and even this may not be useful in some scenarios. For example, in onBindViewHolder, we can write the corresponding logic according to the position of the data. As I did above, I need to remove the layout_margin corresponding to the last data. This is also perfectly fine, but if we use this method, First, if we set layout_margin to each item, then we will reuse this XML file in the future, which we will not be able to reuse due to the different spacing, or reuse will need to be controlled in the code. If you use this, it will be very simple and will no longer use code control in adapter.

There are two steps to using this:

  1. Realize one’s ownItemDecorationSubclass.
  2. Added to therecyclerView

1. Make yoursItemDecorationA subclass

This class in androidx. Recyclerview. Widget. Recyclerview. ItemDecoration below:

class ItemSeparatorDecoration: RecyclerView.ItemDecoration(a)Copy the code

Here we have the source code for ItemDecoration. I will delete all the apis that will be deprecated:

abstract class ItemDecoration {
    public void onDraw(Canvas c, RecyclerView parent, State state) {}
    public void onDrawOver(Canvas c, RecyclerView parent, State state) {}
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {}}Copy the code

Finding that we can rewrite these three functions, here’s what they mean:

1) Void onDraw(Canvas C, RecyclerView parent, State State)

Meanings of parameters:

  • Canvas C Canvas draws objects
  • RecyclerView Parent RecyclerView object itself
  • State State Indicates the current RecyclerView status

And what it does is it draws, it can draw anywhere, but if you just want to draw into each of these terms, then you have to figure out where it is.

2) void onDrawOver(Canvas C, RecyclerView parent, State State)

Just like above, the difference is that what’s drawn is always on top, so what’s drawn is not covered.

3) Void getItemOffsets(Rect outRect, View View, RecyclerView parent, State State)

Meanings of parameters:

  • Rect outRect Item The distance object around it
  • View View Indicates the current View
  • RecyclerView parent RecyclerView itself
  • State State RecyclerView status

Here you can set the distance between item and RecyclerView. And just to clarify, what do I mean by the distance to the sides?

2. Implement the above interval

The interval is the easiest, because we just need to rewrite the getItemOffsets function, which is called when each item is drawn, so in this case we only need to deal with the interval of each item. Here’s the rewrite code. Note that the unit here is not DP, it’s PX, so if we need to use dp, If you don’t know how to convert, you can define dp to dimen.xml and get it directly from your code:

context.resources.getDimensionPixelSize(R.dimen.test_16dp)
Copy the code

Where r.dipen.test_16dp is the value you defined.

GetItemOffsets: getItemOffsets

override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        super.getItemOffsets(outRect, view, parent, state)
        if(parent.getChildLayoutPosition(view) ! =0) {
            outRect.top = context.resources.getDimensionPixelSize(R.dimen.test_10dp)
        }
    }
Copy the code

Have you found very simple, so you can achieve the above effect, but the most common should still be the partition line.

3. Implement the dividing line

Look at the code:

class MyItemDivider(val context: Context, orientation: Int) : RecyclerView.ItemDecoration() {
    companion object {
        // The attr of the splitter
        private val ATTRS = intArrayOf(android.R.attr.listDivider)
        const val HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL
        const val VERTICAL_LIST = LinearLayoutManager.VERTICAL
    }

    // Drawa Drawable on a Canvas
    private var mDivider: Drawable? = null
    private var mOrientation: Int? = null

    init {
        val a = context.obtainStyledAttributes(ATTRS)
        mDivider = a.getDrawable(0)
        a.recycle()
        setOrientation(orientation)
    }

    /** * Set VERTICAL_LIST if RecyclerView is used up or down, otherwise set HORIZONTAL_LIST@paramOrientation direction * /
    private fun setOrientation(orientation: Int) {
        // The value passed in must be predefined
        if(orientation ! = HORIZONTAL_LIST && orientation ! = VERTICAL_LIST) {throw IllegalArgumentException("invalid orientation")
        }
        mOrientation = orientation
    }

    /** * starts drawing, this function is executed only once, * so we need to draw all items here, * instead of just one item */
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent)
        } else {
            drawHorizontal(c, parent)
        }
    }

    private fun drawHorizontal(c: Canvas, parent: RecyclerView) {
        val top = parent.paddingTop
        val bottom = parent.height - parent.paddingBottom
        val childCount = parent.childCount
        for (i in 0 until childCount) {
            val child = parent.getChildAt(i)
            val params = child.layoutParams as RecyclerView.LayoutParams
            val left = child.right + params.rightMargin
            valright = left + (mDivider? .intrinsicWidth ?:0) mDivider? .setBounds(left, top, right, bottom) mDivider? .draw(c) } }private fun drawVertical(c: Canvas, parent: RecyclerView) {
        // The distance to the left,
        // this means where to draw the left side,
        // For each term,
        // You must remove the paddingLeft of RecyclerView
        val left = parent.paddingLeft
        // The width of RecyclerView minus the paddingRight value of RecyclerView
        val right = parent.width - parent.paddingRight
        // Get the number of items in RecyclerView
        val childCount = parent.childCount
        // The loop completes each item, and if the last item is not needed, then the loop will loop one less time
        for (i in 0 until childCount) {
            val child = parent.getChildAt(i)
            val params = child.layoutParams as RecyclerView.LayoutParams
            // The distance above is the default marginBottom below the current Item
            val top = child.bottom + params.bottomMargin
            // The bottom line is simple: the top + the height of the dividing line
            valbottom = top + (mDivider? .intrinsicHeight ?:0) mDivider? .setBounds(left, top, right, bottom) mDivider? .draw(c) } }// This function is executed repeatedly, the same number of times as the number of items
    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        super.getItemOffsets(outRect, view, parent, state)
        // Since we draw at the distance above, but actually there is no active space for us to draw,
        // We need to override this function to manually adjust the space so that the drawing above is not overwritten
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0.0.0, mDivider? .intrinsicHeight ?:0)}else {
            outRect.set(0.0, mDivider? .intrinsicWidth ?:0.0)}}}Copy the code

The code comes from Liu Wangshu’s trilogy, and I have explained and explained the code. If you don’t understand the distance part of the code, you can just look at the diagram below.

Pay attention totopI’m only marking the distance from the current Item, but it’s not, it’s actually the distance from the top of the Item. I’m marking it this way to be consistent with the code; If the red box above is the dividing line we want to draw, then the value we want to get corresponds to the annotation above. generalonDrawgetItemOffsetsUse it together. If not, you won’t be able to see it, and if you do, it’s not normal. For the reasons I mentioned above,onDrawThe drawing is drawn below the Item, so if there is not enough space, the result is that the drawing is invisible.

Content will be added, at the same time about RecyclerView in the future launched, really complete strategy, from use to problem solving to source analysis.