前言
我基本上找遍了網上所有通過ItemDecoration
設定分隔線的文章,但都不盡如意,它們大多隻適用於部分情況,比如只能給線性佈局設定、只能設定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,分別獲取它們的位置資訊,然後分別的繪製對應的分割線。 -- 參考:juejin.im/post/5cecef…
示例圖:
SpacesItemDecoration | GridSpaceItemDecoration |
---|---|
引數配置
SpacesItemDecoration
構造方法有四個:
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.
* @param orientation 水平方向or垂直方向,預設SpacesItemDecoration.VERTICAL
* @param headerNoShowSize 不顯示分割線的item個數 這裡應該包含重新整理頭
* @param footerNoShowSize 尾部 不顯示分割線的item個數 預設不顯示最後一個item的分割線
*/
public SpacesItemDecoration(Context context, int orientation, int headerNoShowSize, int footerNoShowSize)
複製程式碼
其他引數設定,其中setDrawable
與setParam
只能選擇其一:
/**
* 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 spacing item 間距
* @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--------
* 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 顯示規則
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--------
* 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) {
// 超過第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 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);
// 過濾到頭部不顯示的分割線
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 spacing item 間距
* @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--------
* 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 顯示規則
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--------
* 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) {
// 超過第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;
}
}
複製程式碼
總結一下
這兩個類SpacesItemDecoration、GridSpaceItemDecoration 基本涵蓋了所有列表的情況,如果有一些特殊的需求在上面稍微擴充一下就好,它們收錄在本人開源的一個RecyclerView開源庫裡:youlookwhat/ByRecyclerView。如有其他問題,歡迎留言騷擾~