最近在使用同事写的GridSpaceItemDecoratoin时发现不太好用,准备自己重新写一个,于是在网上找资源
在Google找了不少资料,并不如意,故写下此篇
以这位大神的文章为蓝本,地址如下
Android RecyclerView 使用完全解析 体验艺术般的控件
大家可以先看一下,ps:个人不太习惯把别人写的很好的东西重新写一篇,直接引用了
先声明,文章并无意冒犯这位大神,仅想分享一下自己的方案,望共勉
文章的竖向列表的divider是没有问题的,但是网格布局的divider使用,是有问题,让我们先看一下Item偏移代码
@Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部 { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边 { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } }
代码逻辑是:最后一行不做底部偏移,最后一列不做右边偏移,其它都做右边和底部偏移,由此,最后一列的Item的宽度会比其它列要大Divider的尺寸
误处分析
一个spanCount列的列表,加上divider后,一行Item减少的宽度为 (divider.size*spanCount-1)
而代码中把这一行所减少的宽度平摊在0 - (spanCount-2)位置的Item上,最后一列未做减少,故导致最后一列Item变大
解决方案逻辑
一个spanCount列的列表,加上divider后,一行Item减少的宽度为 dividerSize*(spanCount-1)
则每个Item需减少 dividerSize*(spanCount-1)/spanCount
相邻Item间距必须 是 dividerSize
第一列Item的左边和最后一列Item的右边偏移必须是0
依以上3条得出左右偏移量公式
left = column * dividerSize / spanCount;
right = dividerSize - (column + 1) * dividerSize / spanCount;
ps: column 为Item在该行的位置
附上完整代码
package com.senba.used.support.view.customRecycleView;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.support.annotation.ColorRes;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.RecyclerView.ItemDecoration;import android.support.v7.widget.StaggeredGridLayoutManager;import android.view.View;/** * Created by Mark on 2017/6/2. */public class GridItemDecoration extends ItemDecoration { Paint mVerPaint, mHorPaint; Builder mBuilder; public GridItemDecoration(Builder builder) { init(builder); } void init(Builder builder) { this.mBuilder = builder; mVerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mVerPaint.setStyle(Paint.Style.FILL); mVerPaint.setColor(builder.verColor); mHorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mHorPaint.setStyle(Paint.Style.FILL); mHorPaint.setColor(builder.horColor); } private void drawHorizontal(Canvas c, RecyclerView parent) { int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { if (mBuilder.isExistHeadView && i == 0) continue; View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int left = child.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mBuilder.dividerVerSize; final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mBuilder.dividerHorSize; c.drawRect(left, top, right, bottom, mHorPaint); } } private void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { if (mBuilder.isExistHeadView && i == 0) continue; final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + mBuilder.dividerVerSize; c.drawRect(left, top, right, bottom, mVerPaint); } } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount) return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); // StaggeredGridLayoutManager 且纵向滚动 if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; if (pos >= childCount) return true; } else // StaggeredGridLayoutManager 且横向滚动 { if ((pos + 1) % spanCount == 0) { return true; } } } return false; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); drawHorizontal(c, parent); drawVertical(c, parent); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(); if (mBuilder.isExistHeadView) itemPosition -= 1; if (itemPosition < 0) return; int column = itemPosition % spanCount; int bottom = 0; int left = column * mBuilder.dividerVerSize / spanCount; int right = mBuilder.dividerVerSize - (column + 1) * mBuilder.dividerVerSize / spanCount; if (!(isLastRaw(parent, itemPosition, spanCount, childCount) && !mBuilder.isShowLastDivider)) bottom = mBuilder.dividerHorSize; outRect.set(left, 0, right, bottom); marginOffsets(outRect, spanCount, itemPosition); } private void marginOffsets(Rect outRect, int spanCount, int itemPosition) { if (mBuilder.marginRight == 0 && mBuilder.marginLeft == 0) return; int itemShrink = (mBuilder.marginLeft + mBuilder.marginRight) / spanCount; outRect.left += (mBuilder.marginLeft - (itemPosition % spanCount) * itemShrink); outRect.right += ((itemPosition % spanCount) + 1) * itemShrink - mBuilder.marginLeft; } public static class Builder { private Context c; int horColor; int verColor; int dividerHorSize; int dividerVerSize; int marginLeft, marginRight; boolean isShowLastDivider = false; boolean isExistHeadView = false; public Builder(Context c) { this.c = c; } /** * 设置divider的颜色 * * @param color * @return */ public Builder color(@ColorRes int color) { this.horColor = c.getResources().getColor(color); this.verColor = c.getResources().getColor(color); return this; } /** * 单独设置横向divider的颜色 * * @param horColor * @return */ public Builder horColor(@ColorRes int horColor) { this.horColor = c.getResources().getColor(horColor); return this; } /** * 单独设置纵向divider的颜色 * * @param verColor * @return */ public Builder verColor(@ColorRes int verColor) { this.verColor = c.getResources().getColor(verColor); return this; } /** * 设置divider的宽度 * * @param size * @return */ public Builder size(int size) { this.dividerHorSize = size; this.dividerVerSize = size; return this; } /** * 设置横向divider的宽度 * * @param horSize * @return */ public Builder horSize(int horSize) { this.dividerHorSize = horSize; return this; } /** * 设置纵向divider的宽度 * * @param verSize * @return */ public Builder verSize(int verSize) { this.dividerVerSize = verSize; return this; } /** * 设置剔除HeadView的RecyclerView左右两边的外间距 * * @param marginLeft * @param marginRight * @return */ public Builder margin(int marginLeft, int marginRight) { this.marginLeft = marginLeft; this.marginRight = marginRight; return this; } /** * 最后一行divider是否需要显示 * * @param isShow * @return */ public Builder showLastDivider(boolean isShow) { this.isShowLastDivider = isShow; return this; } /** * 是否包含HeadView * * @param isExistHead * @return */ public Builder isExistHead(boolean isExistHead) { this.isExistHeadView = isExistHead; return this; } public GridItemDecoration build() { return new GridItemDecoration(this); } } }
如有问题 欢迎指正