preface

I have basically found all the articles on the Internet to set the dividing line through ItemDecoration, but they are not satisfactory. Most of them are only suitable for some cases, such as only linear layout setting, only color setting but not drawable setting, can not remove the dividing line of HeaderView part, configuration trouble, etc.

So I took great pains to come up with two classes: SpacesItemDecoration and GridSpaceItemDecoration. They basically solve all of the above problems!

  • Included in open source project: ByRecyclerView

What do they do

SpacesItemDecoration:

Give LinearLayoutManager set

  • 1, can set color or drawable
  • 2, can set the left and right or up and down the separation line spacing
  • 3, You can set header or footer not to display the number of dividers, function like ListView setHeaderDividersEnabled(ture)
  • 4. Support horizontal or vertical

GridSpaceItemDecoration:

Give GridLayoutManager or StaggeredGridLayoutManager Settings

  • 1, can be configured only around whether to display the dividing line
  • 2. You can set header or footer not to display the number of dividing lines

Drawing principle:

The getItemOffsets() method sets the offset of the width of the item, and the onDraw() method draws the color of the line. The getItemOffsets are for each ItemView, while the onDraw method is for RecyclerView itself, so the onDraw method needs to traverse the visible ItemView on the screen to obtain their location information respectively. Then draw the corresponding dividing line respectively. — Reference: juejin.cn/post/684490…

Figure:

SpacesItemDecoration GridSpaceItemDecoration

Parameter configuration

SpacesItemDecoration

There are four construction methods:

SpacesItemDecoration(Context context)
SpacesItemDecoration(Context context, int orientation)
SpacesItemDecoration(Context context, int orientation, int headerNoShowSize)
/ * * *@param context          Current context, it will be used to access resources.
 * @paramHorizontal or VERTICAL orientation, the default SpacesItemDecoration. VERTICAL *@paramHeaderNoShowSize Does not display the number of splitter items. This should include the refresh header *@paramFooterNoShowSize Specifies the number of items that do not display the dividing line at the end of the item
public SpacesItemDecoration(Context context, int orientation, int headerNoShowSize, int footerNoShowSize)
Copy the code

Other parameter Settings, setDrawable and setParam can only be selected:

/**
 * Sets the {@link Drawable} for this divider.
 *
 * @param drawable Drawable that should be used as a divider.
 */
public SpacesItemDecoration setDrawable(Drawable drawable)

/** * set the line color directly, etc. Do not set drawable * * @param dividerColor Dividing line color * @Param dividerSpacing dividing line spacing * @Param leftTopPaddingDp If it is landscape - Left margin * If it is portrait - Top margin @param rightBottomPaddingDp if it's landscape - right margin if it's portrait - bottom margin */
public SpacesItemDecoration setParam(int dividerColor, int dividerSpacing, float leftTopPaddingDp, float rightBottomPaddingDp)
Copy the code

A complete setup is as follows:

// Set the dividing line color
SpacesItemDecoration itemDecoration = new SpacesItemDecoration(recyclerView.getContext(), SpacesItemDecoration.VERTICAL, 0.1)
    .setParam(R.color.colorLine, 1.12.12);
recyclerView.addItemDecoration(itemDecoration);
        
// Set the line drawable
SpacesItemDecoration itemDecoration = new SpacesItemDecoration(recyclerView.getContext(), SpacesItemDecoration.VERTICAL, 0.1)
    .setDrawable(R.drawable.shape_line);
recyclerView.addItemDecoration(itemDecoration);
Copy the code

The core code

Here mainly explains the core code of these parameters configuration, please see the source code directly for details:

for (int i = 0; i < childCount; i++) {
    final View child = parent.getChildAt(i);
    final int childRealPosition = parent.getChildAdapterPosition(child);

    // Filter to the divider line not shown in the header
    if (childRealPosition < mHeaderNoShowSize) {
        continue;
    }
    // Filter to the divider line not shown at the end
    if (childRealPosition <= lastPosition - mFooterNoShowSize) {

        / / set the drawable
        if(mDivider ! =null) {
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }

        / / set the color
        if(mPaint ! =null) {
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            // Spacing between heads and tails
            int left1 = left + mLeftTopPadding;
            int right1 = right - mRightBottomPadding;
            int top1 = child.getBottom() + params.bottomMargin;
            intbottom1 = top1 + mDividerSpacing; canvas.drawRect(left1, top1, right1, bottom1, mPaint); }}}Copy the code

GridSpaceItemDecoration

There are two constructors:

GridSpaceItemDecoration(int spacing)
/ * * *@paramSpacing for spacing item *@paramIncludeEdge Item Is also spaced around the screen */
public GridSpaceItemDecoration(int spacing, boolean includeEdge)
Copy the code

Other parameter Settings:

/** * sets the position from which to end the spacing **@paramStartFromSize is usually set to the number of headerViews + refresh layout (not necessarily set) *@paramEndFromSize defaults to 1, usually the number of footerViews + loading more layouts (not set) */
public GridSpaceItemDecoration setNoShowSpace(int startFromSize, int endFromSize)
Copy the code

Complete Settings are as follows:

GridSpaceItemDecoration itemDecoration = new GridSpaceItemDecoration(5.true)
        .setNoShowSpace(1.1);
recyclerView.addItemDecoration(itemDecoration);
Copy the code

The core code

int lastPosition = state.getItemCount() - 1;
int position = parent.getChildAdapterPosition(view);
if (mStartFromSize <= position && position <= lastPosition - mEndFromSize) {

    / / line
    int spanGroupIndex = -1;
    / / column
    int column = 0;
    // Whether the waterfall flows in a row
    boolean fullSpan = false;

    RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    if (layoutManager instanceof GridLayoutManager) {
        GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
        GridLayoutManager.SpanSizeLookup spanSizeLookup = gridLayoutManager.getSpanSizeLookup();
        int spanCount = gridLayoutManager.getSpanCount();
        // spanSize of the current position
        int spanSize = spanSizeLookup.getSpanSize(position);
        // Several in a row
        mSpanCount = spanCount / spanSize;
        // =0 means it's the leftmost 0, 2, 4
        int spanIndex = spanSizeLookup.getSpanIndex(position, spanCount);
        / / column
        column = spanIndex / spanSize;
        // Subtract mStartFromSize from the line to get the line starting at 0
        spanGroupIndex = spanSizeLookup.getSpanGroupIndex(position, spanCount) - mStartFromSize;

    } else if (layoutManager instanceof StaggeredGridLayoutManager) {
        // Waterfall streams get columns differently
        StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
        / / column
        column = params.getSpanIndex();
        // Is there a full line
        fullSpan = params.isFullSpan();
        mSpanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
    }
    // Subtract position without spacing to get position starting from 0
    position = position - mStartFromSize;

    if (mIncludeEdge) {
        /* * Example: * spacing = 10; spanCount = 3 * ---------10-------- * 10 3+7 6+4 10 * ---------10-------- * 10 3+7 6+4 10 * ---------10-------- */
        if (fullSpan) {
            outRect.left = 0;
            outRect.right = 0;
        } else {
            outRect.left = mSpacing - column * mSpacing / mSpanCount;
            outRect.right = (column + 1) * mSpacing / mSpanCount;
        }

        if (spanGroupIndex > -1) {
            // Grid displays rules
            if (spanGroupIndex < 1 && position < mSpanCount) {
                // The first line has the upper spacingoutRect.top = mSpacing; }}else {
            if (fullPosition == -1 && position < mSpanCount && fullSpan) {
                // Find position on the first full line in the header
                fullPosition = position;
            }
            // Stagger displays the rule header without a full line or the header experiences the full line but spacing on the previous position display
            boolean isFirstLineStagger = (fullPosition == -1 || position < fullPosition) && (position < mSpanCount);
            if (isFirstLineStagger) {
                // The first line has the upper spacing
                outRect.top = mSpacing;
            }
        }

        outRect.bottom = mSpacing;

    } else {
        /* * Example: * spacing = 10; spanCount = 3 * --------0-------- * 0 3+7 6+4 0 * -------10-------- * 0 3+7 6+4 0 * --------0-------- */
        if (fullSpan) {
            outRect.left = 0;
            outRect.right = 0;
        } else {
            outRect.left = column * mSpacing / mSpanCount;
            outRect.right = mSpacing - (column + 1) * mSpacing / mSpanCount;
        }

        if (spanGroupIndex > -1) {
            if (spanGroupIndex >= 1) {
                // all lines beyond line 0 are displayed as upper spacingoutRect.top = mSpacing; }}else {
            if (fullPosition == -1 && position < mSpanCount && fullSpan) {
                // Find position on the first full line in the header
                fullPosition = position;
            }
            // Stagger on the spacing display rule
            booleanisStaggerShowTop = position >= mSpanCount || (fullSpan && position ! =0) || (fullPosition ! = -1&& position ! =0);

            if (isStaggerShowTop) {
                // all lines beyond line 0 are displayed as upper spacingoutRect.top = mSpacing; }}}}Copy the code

The complete code

SpacesItemDecoration:

/** * Add a splitter line to the LinearLayoutManager@author jingbin
 * https://github.com/youlookwhat/ByRecyclerView
 */
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {

    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    public static final int VERTICAL = LinearLayout.VERTICAL;
    private static final String TAG = "itemDivider";
    private Context mContext;
    private Drawable mDivider;
    private Rect mBounds = new Rect();
    /** * Configure android in the AppTheme :listDivider */
    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
    /** * the header does not display the number of items on the splitter line. The header should contain the refresh header, * for example, if there is a headerView and a pull-down refresh, pass 2 */
    private int mHeaderNoShowSize = 0;
    By default, the last item's line is not displayed */
    private int mFooterNoShowSize = 1;
    /**
     * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
     */
    private int mOrientation;
    private Paint mPaint;
    /** * if it is horizontal - width * if it is vertical - height */
    private int mDividerSpacing;
    /** * if it is horizontal - left margin * if it is vertical - top margin */
    private int mLeftTopPadding;
    /** ** if it is horizontal - right distance * if it is vertical - bottom distance */
    private int mRightBottomPadding;
    private ByRecyclerView byRecyclerView;

    public SpacesItemDecoration(Context context) {
        this(context, VERTICAL, 0.1);
    }

    public SpacesItemDecoration(Context context, int orientation) {
        this(context, orientation, 0.1);
    }

    public SpacesItemDecoration(Context context, int orientation, int headerNoShowSize) {
        this(context, orientation, headerNoShowSize, 1);
    }

    /**
     * Creates a divider {@link RecyclerView.ItemDecoration}
     *
     * @param context          Current context, it will be used to access resources.
     * @param orientation      Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
     * @param headerNoShowSize headerViewSize + RefreshViewSize
     * @param footerNoShowSize footerViewSize
     */
    public SpacesItemDecoration(Context context, int orientation, int headerNoShowSize, int footerNoShowSize) {
        mContext = context;
        mHeaderNoShowSize = headerNoShowSize;
        mFooterNoShowSize = footerNoShowSize;
        setOrientation(orientation);
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
    }

    /**
     * Sets the orientation for this divider. This should be called if
     * {@link RecyclerView.LayoutManager} changes orientation.
     *
     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
     */
    public SpacesItemDecoration setOrientation(int orientation) {
        if(orientation ! = HORIZONTAL && orientation ! = VERTICAL) {throw new IllegalArgumentException("Invalid orientation. It should be either HORIZONTAL or VERTICAL");
        }
        mOrientation = orientation;
        return this;
    }

    /**
     * Sets the {@link Drawable} for this divider.
     *
     * @param drawable Drawable that should be used as a divider.
     */
    public SpacesItemDecoration setDrawable(Drawable drawable) {
        if (drawable == null) {
            throw new IllegalArgumentException("drawable cannot be null.");
        }
        mDivider = drawable;
        return this;
    }

    public SpacesItemDecoration setDrawable(@DrawableRes int id) {
        setDrawable(ContextCompat.getDrawable(mContext, id));
        return this;
    }

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        if (parent.getLayoutManager() == null || (mDivider == null && mPaint == null)) {
            return;
        }
        if (mOrientation == VERTICAL) {
            drawVertical(canvas, parent, state);
        } else{ drawHorizontal(canvas, parent, state); }}private void drawVertical(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        canvas.save();
        final int left;
        final int right;
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.getClipToPadding()) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            canvas.clipRect(left, parent.getPaddingTop(), right, parent.getHeight() - parent.getPaddingBottom());
        } else {
            left = 0;
            right = parent.getWidth();
        }

        final int childCount = parent.getChildCount();
        final int lastPosition = state.getItemCount() - 1;
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final int childRealPosition = parent.getChildAdapterPosition(child);

            // Filter to the divider line not shown in the header
            if (childRealPosition < mHeaderNoShowSize) {
                continue;
            }
            // Filter to the divider line not shown at the end
            if (childRealPosition <= lastPosition - mFooterNoShowSize) {
                if(mDivider ! =null) {
                    parent.getDecoratedBoundsWithMargins(child, mBounds);
                    final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
                    final int top = bottom - mDivider.getIntrinsicHeight();
                    mDivider.setBounds(left, top, right, bottom);
                    mDivider.draw(canvas);
                }

                if(mPaint ! =null) {
                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    int left1 = left + mLeftTopPadding;
                    int right1 = right - mRightBottomPadding;
                    int top1 = child.getBottom() + params.bottomMargin;
                    int bottom1 = top1 + mDividerSpacing;
                    canvas.drawRect(left1, top1, right1, bottom1, mPaint);
                }
            }
        }
        canvas.restore();
    }

    private void drawHorizontal(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        canvas.save();
        final int top;
        final int bottom;
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.getClipToPadding()) {
            top = parent.getPaddingTop();
            bottom = parent.getHeight() - parent.getPaddingBottom();
            canvas.clipRect(parent.getPaddingLeft(), top,
                    parent.getWidth() - parent.getPaddingRight(), bottom);
        } else {
            top = 0;
            bottom = parent.getHeight();
        }

        final int childCount = parent.getChildCount();
        final int lastPosition = state.getItemCount() - 1;
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final int childRealPosition = parent.getChildAdapterPosition(child);

            // Filter to the divider line not shown in the header
            if (childRealPosition < mHeaderNoShowSize) {
                continue;
            }
            // Filter to the divider line not shown at the end
            if (childRealPosition <= lastPosition - mFooterNoShowSize) {
                if(mDivider ! =null) {
                    parent.getDecoratedBoundsWithMargins(child, mBounds);
                    final int right = mBounds.right + Math.round(child.getTranslationX());
                    final int left = right - mDivider.getIntrinsicWidth();
                    mDivider.setBounds(left, top, right, bottom);
                    mDivider.draw(canvas);
                }

                if(mPaint ! =null) {
                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    int left1 = child.getRight() + params.rightMargin;
                    int right1 = left1 + mDividerSpacing;
                    int top1 = top + mLeftTopPadding;
                    int bottom1 = bottom - mRightBottomPadding;
                    canvas.drawRect(left1, top1, right1, bottom1, mPaint);
                }
            }
        }
        canvas.restore();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (mDivider == null && mPaint == null) {
            outRect.set(0.0.0.0);
            return;
        }
        //parent.getChildCount() Cannot get the total number of items
        int lastPosition = state.getItemCount() - 1;
        int position = parent.getChildAdapterPosition(view);

        boolean mScrollTopFix = false;
        if (byRecyclerView == null && parent instanceof ByRecyclerView) {
            byRecyclerView = (ByRecyclerView) parent;
        }
        if(byRecyclerView ! =null && byRecyclerView.isRefreshEnabled()) {
            mScrollTopFix = true;
        }

        // The scroll bar is at the top
        boolean isFixScrollTop = mScrollTopFix && position == 0;
        boolean isShowDivider = mHeaderNoShowSize <= position && position <= lastPosition - mFooterNoShowSize;

        if (mOrientation == VERTICAL) {
            if (isFixScrollTop) {
                outRect.set(0.0.0.1);
            } else if (isShowDivider) {
                outRect.set(0.0.0, mDivider ! =null ? mDivider.getIntrinsicHeight() : mDividerSpacing);
            } else {
                outRect.set(0.0.0.0); }}else {
            if (isFixScrollTop) {
                outRect.set(0.0.1.0);
            } else if (isShowDivider) {
                outRect.set(0.0, mDivider ! =null ? mDivider.getIntrinsicWidth() : mDividerSpacing, 0);
            } else {
                outRect.set(0.0.0.0); }}}/** * Sets the position and number of item to not show the splitter line **@paramHeaderNoShowSize Specifies the number of items * that do not display the dividing line@paramFooterNoShowSize Does not display the number of items on the end of the dividing line. The default is 1. The last item is not displayed
    public SpacesItemDecoration setNoShowDivider(int headerNoShowSize, int footerNoShowSize) {
        this.mHeaderNoShowSize = headerNoShowSize;
        this.mFooterNoShowSize = footerNoShowSize;
        return this;
    }

    /** * Sets the number of items that do not display the header splitter **@paramHeaderNoShowSize Does not display the number of items for the splitter */
    public SpacesItemDecoration setHeaderNoShowDivider(int headerNoShowSize) {
        this.mHeaderNoShowSize = headerNoShowSize;
        return this;
    }

    public SpacesItemDecoration setParam(int dividerColor, int dividerSpacing) {
        return setParam(dividerColor, dividerSpacing, 0.0);
    }

    /** * drawable **@paramDividerColor The dividing line color *@paramDividerSpacing dividerSpacing *@paramLeftTopPaddingDp if it is landscape - left margin * if it is portrait - top margin *@paramRightBottomPaddingDp if it's landscape - right margin * if it's portrait - bottom margin */
    public SpacesItemDecoration setParam(int dividerColor, int dividerSpacing, float leftTopPaddingDp, float rightBottomPaddingDp) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(ContextCompat.getColor(mContext, dividerColor));
        mDividerSpacing = dividerSpacing;
        mLeftTopPadding = dip2px(leftTopPaddingDp);
        mRightBottomPadding = dip2px(rightBottomPaddingDp);
        mDivider = null;
        return this;
    }

    /** * Switch from dp units to px(pixels) */ based on the phone's resolution
    public int dip2px(float dpValue) {
        final float scale = mContext.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5 f); }}Copy the code

GridSpaceItemDecoration:

/ * * * to GridLayoutManager or StaggeredGridLayoutManager set spacing, can be set up to remove fore and aft spacing number * *@author jingbin
 * https://github.com/youlookwhat/ByRecyclerView
 */

public class GridSpaceItemDecoration extends RecyclerView.ItemDecoration {

    /**
     * 每行个数
     */
    private int mSpanCount;
    /** ** spacing */
    private int mSpacing;
    /** * Is there also space around the screen */
    private boolean mIncludeEdge;
    /** * The header does not display the number of spaced items */
    private int mStartFromSize;
    By default, the spacing of the last item is not processed */
    private int mEndFromSize = 1;
    /** * Position */ for the first full line in the head of the waterfall stream
    private int fullPosition = -1;

    public GridSpaceItemDecoration(int spacing) {
        this(spacing, true);
    }

    / * * *@paramSpacing for spacing item *@paramIncludeEdge Item Is also spaced around the screen */
    public GridSpaceItemDecoration(int spacing, boolean includeEdge) {
        this.mSpacing = spacing;
        this.mIncludeEdge = includeEdge;
    }

    /** * No need to manually set spanCount */
    @Deprecated
    public GridSpaceItemDecoration(int spanCount, int spacing) {
        this(spanCount, spacing, true);
    }

    /** * No need to manually set spanCount */
    @Deprecated
    public GridSpaceItemDecoration(int spanCount, int spacing, boolean includeEdge) {
        this.mSpanCount = spanCount;
        this.mSpacing = spacing;
        this.mIncludeEdge = includeEdge;
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        int lastPosition = state.getItemCount() - 1;
        int position = parent.getChildAdapterPosition(view);
        if (mStartFromSize <= position && position <= lastPosition - mEndFromSize) {

            / / line
            int spanGroupIndex = -1;
            / / column
            int column = 0;
            // Whether the waterfall flows in a row
            boolean fullSpan = false;

            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager) {
                GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
                GridLayoutManager.SpanSizeLookup spanSizeLookup = gridLayoutManager.getSpanSizeLookup();
                int spanCount = gridLayoutManager.getSpanCount();
                // spanSize of the current position
                int spanSize = spanSizeLookup.getSpanSize(position);
                // Several in a row
                mSpanCount = spanCount / spanSize;
                // =0 means it's the leftmost 0, 2, 4
                int spanIndex = spanSizeLookup.getSpanIndex(position, spanCount);
                / / column
                column = spanIndex / spanSize;
                // Subtract mStartFromSize from the line to get the line starting at 0
                spanGroupIndex = spanSizeLookup.getSpanGroupIndex(position, spanCount) - mStartFromSize;

            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                // Waterfall streams get columns differently
                StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
                / / column
                column = params.getSpanIndex();
                // Is there a full line
                fullSpan = params.isFullSpan();
                mSpanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
            }
            // Subtract position without spacing to get position starting from 0
            position = position - mStartFromSize;

            if (mIncludeEdge) {
                /* * Example: * spacing = 10; spanCount = 3 * ---------10-------- * 10 3+7 6+4 10 * ---------10-------- * 10 3+7 6+4 10 * ---------10-------- */
                if (fullSpan) {
                    outRect.left = 0;
                    outRect.right = 0;
                } else {
                    outRect.left = mSpacing - column * mSpacing / mSpanCount;
                    outRect.right = (column + 1) * mSpacing / mSpanCount;
                }

                if (spanGroupIndex > -1) {
                    // Grid displays rules
                    if (spanGroupIndex < 1 && position < mSpanCount) {
                        // The first line has the upper spacingoutRect.top = mSpacing; }}else {
                    if (fullPosition == -1 && position < mSpanCount && fullSpan) {
                        // Find position on the first full line in the header
                        fullPosition = position;
                    }
                    // Stagger displays the rule header without a full line or the header experiences the full line but spacing on the previous position display
                    boolean isFirstLineStagger = (fullPosition == -1 || position < fullPosition) && (position < mSpanCount);
                    if (isFirstLineStagger) {
                        // The first line has the upper spacing
                        outRect.top = mSpacing;
                    }
                }

                outRect.bottom = mSpacing;

            } else {
                /* * Example: * spacing = 10; spanCount = 3 * --------0-------- * 0 3+7 6+4 0 * -------10-------- * 0 3+7 6+4 0 * --------0-------- */
                if (fullSpan) {
                    outRect.left = 0;
                    outRect.right = 0;
                } else {
                    outRect.left = column * mSpacing / mSpanCount;
                    outRect.right = mSpacing - (column + 1) * mSpacing / mSpanCount;
                }

                if (spanGroupIndex > -1) {
                    if (spanGroupIndex >= 1) {
                        // all lines beyond line 0 are displayed as upper spacingoutRect.top = mSpacing; }}else {
                    if (fullPosition == -1 && position < mSpanCount && fullSpan) {
                        // Find position on the first full line in the header
                        fullPosition = position;
                    }
                    // Stagger on the spacing display rule
                    booleanisStaggerShowTop = position >= mSpanCount || (fullSpan && position ! =0) || (fullPosition ! = -1&& position ! =0);

                    if (isStaggerShowTop) {
                        // all lines beyond line 0 are displayed as upper spacing
                        outRect.top = mSpacing;
                    }
                }
            }
        }
    }

    /** * Sets the position from which to set the spacing **@paramStartFromSize is usually set to the number of headerViews + refresh layout (not necessarily set) */
    public GridSpaceItemDecoration setStartFrom(int startFromSize) {
        this.mStartFromSize = startFromSize;
        return this;
    }

    /** * sets the position from which to end the spacing. The default is 1, and the default user has set pull-up loading to * *@paramEndFromSize is usually set to the number of footerViews + loading more layouts */
    public GridSpaceItemDecoration setEndFromSize(int endFromSize) {
        this.mEndFromSize = endFromSize;
        return this;
    }

    /** * sets the position from which to end the spacing **@paramStartFromSize is usually set to the number of headerViews + refresh layout (not necessarily set) *@paramEndFromSize defaults to 1, usually the number of footerViews + loading more layouts (not set) */
    public GridSpaceItemDecoration setNoShowSpace(int startFromSize, int endFromSize) {
        this.mStartFromSize = startFromSize;
        this.mEndFromSize = endFromSize;
        return this; }}Copy the code

To summarize

The two classes of SpacesItemDecoration and GridSpaceItemDecoration basically cover all list situations, if there are some special needs in the above slightly expanded, they are included in a RecyclerView open source library of my open source: Youlookwhat/ByRecyclerView. If you have any other questions, please leave a message