ByRecyclerView(真·万能分割线|ByRecyclerView:真·万能分割线 (线性/宫格/瀑布流))

前言 我基本上找遍了网上所有通过ItemDecorationd设置分隔线的文章,但都不尽如意,它们大多只适用于部分情况,比如只能给线性布局设置、只能设置color不能设置drawable、不能去除HeaderView部分的分割线、配置麻烦等等等。
于是我费尽周折出了两个类:SpacesItemDecoration,GridSpaceItemDecoration。它们基本解决了上述所有问题!

  • 收录于开源项目:ByRecyclerView
它们有什么功能 SpacesItemDecoration:
给LinearLayoutManager设置
  • 1、可设置color或drawable
  • 2、可设置分割线左右或上下的间距
  • 3、可设置header或footer不显示分割线的个数,功能似ListView的setHeaderDividersEnabled(ture)
  • 4、支持横向或纵向
GridSpaceItemDecoration:
给GridLayoutManager或StaggeredGridLayoutManager设置
  • 1、可配置只在四周是否显示分割线
  • 2、可设置header或footer不显示分割线的个数
绘制原理:
网上很多解释通过ItemDecoration绘制分割线的原理的文章,我简单总结一下,在getItemOffsets()方法里设置item宽度的偏移量,在onDraw()方法里主要绘制分割线颜色。getItemOffsets 是针对每一个 ItemView,而 onDraw 方法却是针对 RecyclerView 本身,所以在 onDraw 方法中需要遍历屏幕上可见的 ItemView,分别获取它们的位置信息,然后分别的绘制对应的分割线。 – 参考:https://juejin.im/post/5cecef7d5188250b3a1b9173
示例图:
SpacesItemDecoration GridSpaceItemDecoration
ByRecyclerView(真·万能分割线|ByRecyclerView:真·万能分割线 (线性/宫格/瀑布流))
文章图片
ByRecyclerView(真·万能分割线|ByRecyclerView:真·万能分割线 (线性/宫格/瀑布流))
文章图片
参数配置 SpacesItemDecoration
构造方法有四个:
SpacesItemDecoration(Context context) SpacesItemDecoration(Context context, int orientation) SpacesItemDecoration(Context context, int orientation, int headerNoShowSize) /** * @param contextCurrent context, it will be used to access resources. * @param orientation水平方向or垂直方向,默认SpacesItemDecoration.VERTICAL * @param headerNoShowSize 不显示分割线的item个数 这里应该包含刷新头 * @param footerNoShowSize 尾部 不显示分割线的item个数 默认不显示最后一个item的分割线 */ public SpacesItemDecoration(Context context, int orientation, int headerNoShowSize, int footerNoShowSize)

其他参数设置,其中setDrawablesetParam只能选择其一:
/** * Sets the {@link Drawable} for this divider. * * @param drawable Drawable that should be used as a divider. */ public SpacesItemDecoration setDrawable(Drawable drawable)/** * 直接设置分割线颜色等,不设置drawable * * @param dividerColor分割线颜色 * @param dividerSpacing分割线间距 * @param leftTopPaddingDp如果是横向 - 左边距 *如果是纵向 - 上边距 * @param rightBottomPaddingDp 如果是横向 - 右边距 *如果是纵向 - 下边距 */ public SpacesItemDecoration setParam(int dividerColor, int dividerSpacing, float leftTopPaddingDp, float rightBottomPaddingDp)

一个完整的设置如下:
// 设置分割线color SpacesItemDecoration itemDecoration = new SpacesItemDecoration(recyclerView.getContext(), SpacesItemDecoration.VERTICAL, 0, 1) .setParam(R.color.colorLine, 1, 12, 12); recyclerView.addItemDecoration(itemDecoration); // 设置分割线drawable SpacesItemDecoration itemDecoration = new SpacesItemDecoration(recyclerView.getContext(), SpacesItemDecoration.VERTICAL, 0, 1) .setDrawable(R.drawable.shape_line); recyclerView.addItemDecoration(itemDecoration);

核心代码
这里主要解释这几个参数配置的核心代码,具体请直接见源代码:
for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final int childRealPosition = parent.getChildAdapterPosition(child); // 过滤到头部不显示的分割线 if (childRealPosition < mHeaderNoShowSize) { continue; } // 过滤到尾部不显示的分割线 if (childRealPosition <= lastPosition - mFooterNoShowSize) {// 设置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); }// 设置color 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); } } }

GridSpaceItemDecoration
构造方法有两个:
GridSpaceItemDecoration(int spacing) /** * @param spacingitem 间距 * @param includeEdge item 距屏幕周围是否也有间距 */ public GridSpaceItemDecoration(int spacing, boolean includeEdge)

其他参数设置:
/** * 设置从哪个位置 结束设置间距 * * @param startFromSize 一般为HeaderView的个数 + 刷新布局(不一定设置) * @param endFromSize默认为1,一般为FooterView的个数 + 加载更多布局(不一定设置) */ public GridSpaceItemDecoration setNoShowSpace(int startFromSize, int endFromSize)

完整设置如下:
GridSpaceItemDecoration itemDecoration = new GridSpaceItemDecoration(5, true) .setNoShowSpace(1, 1); recyclerView.addItemDecoration(itemDecoration);

核心代码
int lastPosition = state.getItemCount() - 1; int position = parent.getChildAdapterPosition(view); if (mStartFromSize <= position && position <= lastPosition - mEndFromSize) {// 行 int spanGroupIndex = -1; // 列 int column = 0; // 瀑布流是否占满一行 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(); // 当前position的spanSize int spanSize = spanSizeLookup.getSpanSize(position); // 一行几个 mSpanCount = spanCount / spanSize; // =0 表示是最左边 0 2 4 int spanIndex = spanSizeLookup.getSpanIndex(position, spanCount); // 列 column = spanIndex / spanSize; // 行 减去mStartFromSize,得到从0开始的行 spanGroupIndex = spanSizeLookup.getSpanGroupIndex(position, spanCount) - mStartFromSize; } else if (layoutManager instanceof StaggeredGridLayoutManager) { // 瀑布流获取列方式不一样 StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); // 列 column = params.getSpanIndex(); // 是否是全一行 fullSpan = params.isFullSpan(); mSpanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount(); } // 减掉不设置间距的position,得到从0开始的position position = position - mStartFromSize; if (mIncludeEdge) { /* *示例: * spacing = 10 ;spanCount = 3 * ---------10-------- * 103+76+410 * ---------10-------- * 103+76+410 * ---------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 显示规则 if (spanGroupIndex < 1 && position < mSpanCount) { // 第一行才有上间距 outRect.top = mSpacing; } } else { if (fullPosition == -1 && position < mSpanCount && fullSpan) { // 找到头部第一个整行的position,后面的上间距都不显示 fullPosition = position; } // Stagger显示规则 头部没有整行或者头部体验整行但是在之前的position显示上间距 boolean isFirstLineStagger = (fullPosition == -1 || position < fullPosition) && (position < mSpanCount); if (isFirstLineStagger) { // 第一行才有上间距 outRect.top = mSpacing; } }outRect.bottom = mSpacing; } else { /* *示例: * spacing = 10 ;spanCount = 3 * --------0-------- * 03+76+40 * -------10-------- * 03+76+40 * --------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) { // 超过第0行都显示上间距 outRect.top = mSpacing; } } else { if (fullPosition == -1 && position < mSpanCount && fullSpan) { // 找到头部第一个整行的position fullPosition = position; } // Stagger上间距显示规则 boolean isStaggerShowTop = position >= mSpanCount || (fullSpan && position != 0) || (fullPosition != -1 && position != 0); if (isStaggerShowTop) { // 超过第0行都显示上间距 outRect.top = mSpacing; } } } }

完整代码 SpacesItemDecoration:
/** * 给 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(); /** * 在AppTheme里配置 android:listDivider */ private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; /** * 头部 不显示分割线的item个数 这里应该包含刷新头, * 比如有一个headerView和有下拉刷新,则这里传 2 */ private int mHeaderNoShowSize = 0; /** * 尾部 不显示分割线的item个数 默认不显示最后一个item的分割线 */ private int mFooterNoShowSize = 1; /** * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}. */ private int mOrientation; private Paint mPaint; /** * 如果是横向 - 宽度 * 如果是纵向 - 高度 */ private int mDividerSpacing; /** * 如果是横向 - 左边距 * 如果是纵向 - 上边距 */ private int mLeftTopPadding; /** * 如果是横向 - 右边距 * 如果是纵向 - 下边距 */ 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 contextCurrent context, it will be used to access resources. * @param orientationDivider 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); // 过滤到头部不显示的分割线 if (childRealPosition < mHeaderNoShowSize) { continue; } // 过滤到尾部不显示的分割线 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); // 过滤到头部不显示的分割线 if (childRealPosition < mHeaderNoShowSize) { continue; } // 过滤到尾部不显示的分割线 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() 不能拿到item的总数 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; }// 滚动条置顶 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); } } }/** * 设置不显示分割线的item位置与个数 * * @param headerNoShowSize 头部 不显示分割线的item个数 * @param footerNoShowSize 尾部 不显示分割线的item个数,默认1,不显示最后一个,最后一个一般为加载更多view */ public SpacesItemDecoration setNoShowDivider(int headerNoShowSize, int footerNoShowSize) { this.mHeaderNoShowSize = headerNoShowSize; this.mFooterNoShowSize = footerNoShowSize; return this; }/** * 设置不显示头部分割线的item个数 * * @param headerNoShowSize 头部 不显示分割线的item个数 */ public SpacesItemDecoration setHeaderNoShowDivider(int headerNoShowSize) { this.mHeaderNoShowSize = headerNoShowSize; return this; }public SpacesItemDecoration setParam(int dividerColor, int dividerSpacing) { return setParam(dividerColor, dividerSpacing, 0, 0); }/** * 直接设置分割线颜色等,不设置drawable * * @param dividerColor分割线颜色 * @param dividerSpacing分割线间距 * @param leftTopPaddingDp如果是横向 - 左边距 *如果是纵向 - 上边距 * @param rightBottomPaddingDp 如果是横向 - 右边距 *如果是纵向 - 下边距 */ 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; }/** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public int dip2px(float dpValue) { final float scale = mContext.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }}

GridSpaceItemDecoration:
/** * 给 GridLayoutManager or StaggeredGridLayoutManager 设置间距,可设置去除首尾间距个数 * * @author jingbin * https://github.com/youlookwhat/ByRecyclerView */public class GridSpaceItemDecoration extends RecyclerView.ItemDecoration {/** * 每行个数 */ private int mSpanCount; /** * 间距 */ private int mSpacing; /** * 距屏幕周围是否也有间距 */ private boolean mIncludeEdge; /** * 头部 不显示间距的item个数 */ private int mStartFromSize; /** * 尾部 不显示间距的item个数 默认不处理最后一个item的间距 */ private int mEndFromSize = 1; /** * 瀑布流 头部第一个整行的position */ private int fullPosition = -1; public GridSpaceItemDecoration(int spacing) { this(spacing, true); }/** * @param spacingitem 间距 * @param includeEdge item 距屏幕周围是否也有间距 */ public GridSpaceItemDecoration(int spacing, boolean includeEdge) { this.mSpacing = spacing; this.mIncludeEdge = includeEdge; }/** * 已不需要手动设置spanCount */ @Deprecated public GridSpaceItemDecoration(int spanCount, int spacing) { this(spanCount, spacing, true); }/** * 已不需要手动设置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) {// 行 int spanGroupIndex = -1; // 列 int column = 0; // 瀑布流是否占满一行 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(); // 当前position的spanSize int spanSize = spanSizeLookup.getSpanSize(position); // 一行几个 mSpanCount = spanCount / spanSize; // =0 表示是最左边 0 2 4 int spanIndex = spanSizeLookup.getSpanIndex(position, spanCount); // 列 column = spanIndex / spanSize; // 行 减去mStartFromSize,得到从0开始的行 spanGroupIndex = spanSizeLookup.getSpanGroupIndex(position, spanCount) - mStartFromSize; } else if (layoutManager instanceof StaggeredGridLayoutManager) { // 瀑布流获取列方式不一样 StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); // 列 column = params.getSpanIndex(); // 是否是全一行 fullSpan = params.isFullSpan(); mSpanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount(); } // 减掉不设置间距的position,得到从0开始的position position = position - mStartFromSize; if (mIncludeEdge) { /* *示例: * spacing = 10 ;spanCount = 3 * ---------10-------- * 103+76+410 * ---------10-------- * 103+76+410 * ---------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 显示规则 if (spanGroupIndex < 1 && position < mSpanCount) { // 第一行才有上间距 outRect.top = mSpacing; } } else { if (fullPosition == -1 && position < mSpanCount && fullSpan) { // 找到头部第一个整行的position,后面的上间距都不显示 fullPosition = position; } // Stagger显示规则 头部没有整行或者头部体验整行但是在之前的position显示上间距 boolean isFirstLineStagger = (fullPosition == -1 || position < fullPosition) && (position < mSpanCount); if (isFirstLineStagger) { // 第一行才有上间距 outRect.top = mSpacing; } }outRect.bottom = mSpacing; } else { /* *示例: * spacing = 10 ;spanCount = 3 * --------0-------- * 03+76+40 * -------10-------- * 03+76+40 * --------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) { // 超过第0行都显示上间距 outRect.top = mSpacing; } } else { if (fullPosition == -1 && position < mSpanCount && fullSpan) { // 找到头部第一个整行的position fullPosition = position; } // Stagger上间距显示规则 boolean isStaggerShowTop = position >= mSpanCount || (fullSpan && position != 0) || (fullPosition != -1 && position != 0); if (isStaggerShowTop) { // 超过第0行都显示上间距 outRect.top = mSpacing; } } } } }/** * 设置从哪个位置 开始设置间距 * * @param startFromSize 一般为HeaderView的个数 + 刷新布局(不一定设置) */ public GridSpaceItemDecoration setStartFrom(int startFromSize) { this.mStartFromSize = startFromSize; return this; }/** * 设置从哪个位置 结束设置间距。默认为1,默认用户设置了上拉加载 * * @param endFromSize 一般为FooterView的个数 + 加载更多布局(不一定设置) */ public GridSpaceItemDecoration setEndFromSize(int endFromSize) { this.mEndFromSize = endFromSize; return this; }/** * 设置从哪个位置 结束设置间距 * * @param startFromSize 一般为HeaderView的个数 + 刷新布局(不一定设置) * @param endFromSize默认为1,一般为FooterView的个数 + 加载更多布局(不一定设置) */ public GridSpaceItemDecoration setNoShowSpace(int startFromSize, int endFromSize) { this.mStartFromSize = startFromSize; this.mEndFromSize = endFromSize; return this; } }

总结一下 【ByRecyclerView(真·万能分割线|ByRecyclerView:真·万能分割线 (线性/宫格/瀑布流))】这两个类SpacesItemDecoration、GridSpaceItemDecoration 基本涵盖了所有列表的情况,如果有一些特殊的需求在上面稍微拓展一下就好,它们收录在本人开源的一个RecyclerView开源库里:youlookwhat/ByRecyclerView。如有其他问题,欢迎留言骚扰~

    推荐阅读