preface

As a common control in Android – RecyclerView. When we use RecyclerView, we will use a variety of LayoutManager (if you do not use LayoutManager, the interface will not display any content), but in general, the effect is as follows, without any spacing, the effect is not very beautiful. It looks crowded.

At this time, some students may come up with a solution. Why don’t I add margin to item? Yes, you can do that. However, there is a better way to do it — use the addItemDecoration method.

What’s is a ItemDecoration?

The official link

Official description:

Add an RecyclerView.ItemDecoration to this RecyclerView. Item decorations can affect both measurement and drawing of individual item views.

Item decorations are ordered. Decorations placed earlier in the list will be run/queried/drawn first for their effects on item views. Padding added to views will be nested; a padding added by an earlier decoration will mean further item decorations in the list will be asked to draw/pad within the previous decoration’s given area.

Translation:

To add a RecyclerView. This RecyclerView ItemDecoration. Item decoration can influence the measurement and drawing of individual item views.

Item decoration is sequential. Decorations placed earlier in the list will first run/query/plot their impact on the project view. Fillers added to the view will be nested; Padding added by earlier decorations will mean that other item decorations in the list will be required to draw/fill in the given area of the previous decorations.


To put it simply, we can realize RecyclerView Item decoration by copying the method in Item decoration, just as the name of Item decoration is: Item decoration.

RecyclerView.ItemDecoration

RecyclerView ItemDecoration is an abstract class, we use is usually directly through an anonymous inner class to new, then rewrite the need to implement method.

There are three methods in ItemDecoration as follows. (Six to be exact, since half of the methods are annotated @deprecated and are no longer recommended)

Ontouch:

		/**
         * 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(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
            onDraw(c, parent);
        }
Copy the code

OnDrawOver:

		/**
         * 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(@NonNull Canvas c, @NonNull RecyclerView parent,
                @NonNull State state) {
            onDrawOver(c, parent);
        }
Copy the code

GetItemOffsets:

    /**
     * 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(@NonNull Rect outRect, @NonNull View view,
            @NonNull RecyclerView parent, @NonNull State state) { getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent); }}Copy the code

Implementation approach

First of all, the commonly used LayoutManager is the LinearLayoutManager and GridLayoutManager, so we are divided into two solutions to achieve.

LinearLayoutManager

VERTICAL

  • The top, left and right sides of the first item are separated by two units, and the bottom is separated by one unit.
  • For the last item, set the spacing at the bottom and left and right by two units, and set the spacing at the top by one unit.
  • For other items, the upper and lower Settings are set to one unit spacing, and the left and right Settings are set to two units spacing.

HORIZONTAL

  • The space above, below and to the left of the first item is two units, and the space to the right is one unit.
  • For the last item, set the spacing above, below and to the right by two units, and set the spacing to the left by one unit.
  • For other items, set the spacing of the upper and lower items to two units, and set the spacing of the left and right items to one unit.

GridLayoutManager

If spanCount is set to 1, then the LinearLayoutManager effect is exactly the same as the LinearLayoutManager effect.

VERTICAL

  • For the leftmost column (not the last row), set two units of spacing on both the top and left, and one unit of spacing on the right.
  • For the rightmost column (not the last row), set two units of spacing on both the top and the right, and one unit of spacing on both the left.
  • For the middle column (not the last row), set two units spacing on the top and one unit spacing on the left and right sides.
  • For the leftmost column of the last row, set two units of spacing at the top and bottom, and one unit of spacing at the right.
  • For the rightmost column of the last row, set the top and bottom two units of spacing, and set the left one unit of spacing.
  • For the middle column of the last row, set the top and bottom columns two units apart, and set the left and right columns one unit apart.

Question: What if there’s only one line? Emmm, handled by default 🤐🤐

HORIZONTAL

Not supported yet.

The specific implementation

I use Kotlin’s extension function to achieve, if the students use Java, please write according to the author’s ideas

RecyclerView.kt

import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

/ * * * {@link ItemDecoration#getItemOffsets(outRect: Rect,view: View,parent: RecyclerView)} or
 * {@linkItemDecoration#getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: Recyclerview.state)}. * A convenient way to evenly divide the LinearLayoutManager spacing */
fun equilibriumAssignmentOfLinear(
    unit: Int,
    outRect: Rect,
    view: View,
    parent: RecyclerView
) {
    // Number of items
    val itemCount = parent.getItemCount()
    // Position of the current item
    val itemPosition = parent.getChildAdapterPosition(view)
    vallayoutManager = parent.checkLinearLayoutManager() ? :return
    // Get the layout direction of the LinearLayoutManager
    val orientation = layoutManager.orientation
    // iterate over all items
    for (index in 0..itemCount) {
        when (itemPosition) {
            // First row/column
            0- > {if (orientation == RecyclerView.VERTICAL) {
                    // First row/column && VERTICAL layout -> special treatment for the bottom of item
                    outRect.top = unit * 2
                    outRect.bottom = unit
                    outRect.left = unit * 2
                    outRect.right = unit * 2
                } else {
                    // First row/column && HORIZONTAL -> Special processing for the right side of item
                    outRect.top = unit * 2
                    outRect.bottom = unit * 2
                    outRect.left = unit * 2
                    outRect.right = unit
                }
            }
            // Last row/column
            itemCount - 1- > {if (orientation == RecyclerView.VERTICAL) {
                    // Last row/column && VERTICAL layout -> special treatment for the top of item
                    outRect.top = unit
                    outRect.bottom = unit * 2
                    outRect.left = unit * 2
                    outRect.right = unit * 2
                } else {
                    // Last row/column && HORIZONTAL -> Handles the left side of the item in a special way
                    outRect.top = unit * 2
                    outRect.bottom = unit * 2
                    outRect.left = unit
                    outRect.right = unit * 2}}// Middle row/column
            else- > {if (orientation == RecyclerView.VERTICAL) {
                    // Middle row/column && VERTICAL layout -> Special handling of top and bottom items
                    outRect.top = unit
                    outRect.bottom = unit
                    outRect.left = unit * 2
                    outRect.right = unit * 2
                } else {
                    HORIZONTAL -> Handles the left and right sides of the item
                    outRect.top = unit * 2
                    outRect.bottom = unit * 2
                    outRect.left = unit
                    outRect.right = unit
                }
            }
        }
    }
}

/ * * * {@link ItemDecoration#getItemOffsets(outRect: Rect,view: View,parent: RecyclerView)} or
 * {@linkItemDecoration#getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: Recyclerview.state)}. * A convenient way to evenly divide GridLayoutManager spacing */
fun equilibriumAssignmentOfGrid(
    unit: Int,
    outRect: Rect,
    view: View,
    parent: RecyclerView
) {
    // Number of items
    val itemCount = parent.getItemCount()
    // The span of the grid layout
    val spanCount = parent.getSpanCount()
    // Position of the current item
    val itemPosition = parent.getChildAdapterPosition(view)
    vallayoutManager = parent.checkGridLayoutManager() ? :return
    if (spanCount < 2) {
        equilibriumAssignmentOfLinear(view = view, unit = unit, parent = parent, outRect = outRect)
        return
    }
    // Get the GridLayoutManager layout direction
    val orientation = layoutManager.orientation
    if (orientation == RecyclerView.HORIZONTAL) {
        // Horizontal GridLayoutManager is not currently supported
        throw UnsupportedOperationException("You can’t set a horizontal grid layout because we don’t support!")}// iterate over all items
    for (index in 0..itemCount) {
        when {
            // The leftmost column
            itemPosition % spanCount == 0 -> {
                outRect.left = unit * 2
                outRect.right = unit
            }
            // The rightmost column
            (itemPosition - (spanCount - 1)) % spanCount == 0 -> {
                outRect.left = unit
                outRect.right = unit * 2
            }
            // Middle column (possibly multiple columns)
            else -> {
                outRect.left = unit
                outRect.right = unit
            }
        }
        outRect.top = unit * 2
        // Determine if it is the last line, and add a separate bottom spacing for the last line
        if (itemPosition in (itemCount - spanCount) until itemCount) {
            outRect.bottom = unit * 2}}}Note: This method only applies to RecyclerView where LayoutManager is GridLayoutManager */
fun RecyclerView.getSpanCount(a): Int {
    vallayoutManager = checkGridLayoutManager() ? :return 0
    return layoutManager.spanCount
}

/** * Returns the number of items in the adapter bound to the parent RecyclerView */
fun RecyclerView.getItemCount(a): Int {
    vallayoutManager = layoutManager ? :return 0
    return layoutManager.itemCount
}

/** * Check the RecyclerView setup for the LinearLayoutManager */
private fun RecyclerView.checkLinearLayoutManager(a): LinearLayoutManager? {
    vallayoutManager = layoutManager ? :return null
    if (layoutManager !is LinearLayoutManager) {
        throw IllegalStateException("Make sure you are using the LinearLayoutManager!")}return layoutManager
}

/** * Check the GridLayoutManager for RecyclerView Settings
private fun RecyclerView.checkGridLayoutManager(a): GridLayoutManager? {
    vallayoutManager = layoutManager ? :return null
    if (layoutManager !is GridLayoutManager) {
        throw IllegalStateException("Make sure you are using the GridLayoutManager!")}return layoutManager
}

Copy the code

Edible way

	recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {

        // Unit spacing (half of the actual spacing)
        private val unit = 4.dp

        override fun getItemOffsets(
            outRect: Rect,
            view: View,
            parent: RecyclerView,
            state: RecyclerView.State
        ) {
            super.getItemOffsets(outRect, view, parent, state)
            equilibriumAssignmentOfGrid(unit, outRect, view, parent)
            // equilibriumAssignmentOfLinear(unit, outRect, view, parent)}})Copy the code

Implementation effect

LinearLayoutManager

GridLayoutManager

This is the first time for digg to publish a post, we welcome your likes, comments, rewards and attention. If there are any mistakes, please criticize and correct them

For more of my articles, please visit mineSun beach personal home page