注意:设置分隔间距在网格布局中会有问题。可以使用新的方法来实现,地址使用 ItemDecoration实现间隔

一、不绘制,通过设置分割来实现 Item 间距,在 Item 布局中就不需要设置 margin 了

效果图:
image.pngimage.png

1.1 使用BaseItemIntervalDecoration来设置间距

  • 1.BaseItemIntervalDecoration ``` public class BaseItemIntervalDecoration extends RecyclerView.ItemDecoration {

    private int divider;//上下item之间 相距的距离 private int dividerHorizontal;//左右item之间 相距的距离 private int marginTop;//第一个距离上面的距离 private int marginBottom;//最后一个距离下面的距离 private int lineCount;//一行多少个 private int marginHorizontal;//距离两边的距离

    public BaseItemIntervalDecoration(int divider, int lineCount) {

    1. this.divider = divider;
    2. this.lineCount = lineCount;
    3. this.dividerHorizontal = divider;//默认不设置和上下的一致

    }

    public void setDividerHorizontal(int dividerHorizontal) {

    1. this.dividerHorizontal = dividerHorizontal;

    }

    public void setMarginHorizontal(int marginHorizontal) {

    1. this.marginHorizontal = marginHorizontal;

    }

    public void setMarginTop(int marginTop) {

    1. this.marginTop = marginTop;

    }

    public void setMarginBottom(int marginBottom) {

    1. this.marginBottom = marginBottom;

    }

    @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

    1. if (parent.getAdapter() != null && lineCount != 0) {
    2. int itemCount = parent.getAdapter().getItemCount();
    3. int position = parent.getChildLayoutPosition(view) + 1;
    4. int lineNum = itemCount % lineCount == 0 ? itemCount / lineCount : itemCount / lineCount + 1;
    5. int positionLineNum = position % lineCount == 0 ? position / lineCount : position / lineCount + 1;
    6. if (positionLineNum == 1) {
    7. outRect.top = marginTop;
    8. }
    9. if (lineNum == positionLineNum) {
    10. outRect.bottom = marginBottom;
    11. } else {
    12. outRect.bottom = divider;
    13. }
    14. } else {
    15. outRect.bottom = marginBottom;
    16. }
    17. if (lineCount != 0) {
    18. if (lineCount == 1) {
    19. outRect.left = marginHorizontal;
    20. outRect.right = marginHorizontal;
    21. } else if (parent.getChildLayoutPosition(view) % lineCount == 0) {
    22. // left
    23. outRect.left = marginHorizontal;
    24. outRect.right = dividerHorizontal / 2;
    25. } else if (parent.getChildLayoutPosition(view) % lineCount == (lineCount - 1)) {
    26. outRect.left = dividerHorizontal / 2;
    27. outRect.right = marginHorizontal;
    28. } else {
    29. outRect.left = dividerHorizontal / 2;
    30. outRect.right = dividerHorizontal / 2;
    31. }
    32. }

    } }

  1. - 2. 使用

//使用 BaseItemIntervalDecoration 设置分割间距 //第一个参数是设置每个 item 之间的间距,后一个参数是一行展示几个 item,线性布局网格布局用法一样 val space = resources.getDimensionPixelOffset(R.dimen.dp_10) val itemDecoration = BaseItemIntervalDecoration(space,1) itemDecoration.setMarginTop(space)//设置顶部间距 itemDecoration.setMarginHorizontal(space)//设置两边间距 itemDecoration.setMarginBottom(space)//设置底部间隔

  1. <a name="KQUaY"></a>
  2. #### 1.2 IntervalDecotation
  3. 只适用于线性布局
  4. - 1. IntervalDecotation
  5. ```kotlin
  6. /**
  7. *@FileName: IntervalDecoration
  8. *@author : Lss Administrator
  9. * @e-mail : kiwilss@163.com
  10. * @time : 2020/8/1
  11. * @desc : { 一般不管是哪个item,左右间距通常都是一样的,
  12. * 不排除有特殊的情况,这里按照常见的情况来处理}
  13. */
  14. class IntervalDecoration(
  15. private val top: Int = 0, private val bottom: Int = 0, private val left: Int = 0, private val right: Int = 0
  16. , private val firstTop: Int = 0, private val firstBottom: Int = bottom, private val firstLeft: Int = left, private val firstRight: Int = right,
  17. private val lastTop: Int = top, private val lastBottom: Int = 0, private val lastLeft: Int = left, private val lastRight: Int = right)
  18. : RecyclerView.ItemDecoration() {
  19. override fun getItemOffsets(
  20. outRect: Rect,
  21. view: View,
  22. parent: RecyclerView,
  23. state: RecyclerView.State
  24. ) {
  25. super.getItemOffsets(outRect, view, parent, state)
  26. //如果是第一个item
  27. if (parent.getChildLayoutPosition(view) == 0) {
  28. //设置四个方向的间距
  29. outRect.top = firstTop
  30. outRect.bottom = firstBottom
  31. outRect.left = firstLeft
  32. outRect.right = firstRight
  33. }else if (parent.getChildLayoutPosition(view) + 1 == parent.adapter?.itemCount){
  34. //最后一个item
  35. outRect.top = lastTop
  36. outRect.bottom = lastBottom
  37. outRect.left = lastLeft
  38. outRect.right =lastRight
  39. }else{
  40. //中间的item
  41. outRect.top = top
  42. outRect.bottom = bottom
  43. outRect.left = left
  44. outRect.right = right
  45. }
  46. }
  47. }
  • 2.使用

    1. //设置分割线,实现item间隔,可以自由组合
    2. it.addItemDecoration(IntervalDecoration(left = dimension,right = dimension,top = dimension))

    1.3.使用 BaseItemDecoration

    1. BaseItemDecoration

      1. class BaseItemDecoration private constructor(private val builder: Builder) : ItemDecoration() {
      2. private var dividerHorizontal: Int = 0//上下item之间 相距的距离vertical
      3. private var marginTop: Int = 0 //第一个距离上面的距离 = 0
      4. private var marginBottom = 0 //最后一个距离下面的距离 = 0
      5. private var marginHorizontal = 0 //距离两边的距离 = 0
      6. private var lineCount: Int = 0
      7. override fun getItemOffsets(
      8. outRect: Rect,
      9. view: View,
      10. parent: RecyclerView,
      11. state: RecyclerView.State
      12. ) {
      13. if (parent.adapter != null && lineCount != 0) {
      14. val itemCount = parent.adapter!!.itemCount
      15. val position = parent.getChildLayoutPosition(view) + 1
      16. val lineNum =
      17. if (itemCount % lineCount == 0) itemCount / lineCount else itemCount / lineCount + 1
      18. val positionLineNum =
      19. if (position % lineCount == 0) position / lineCount else position / lineCount + 1
      20. if (positionLineNum == 1) {
      21. outRect.top = marginTop
      22. }
      23. if (lineNum == positionLineNum) {
      24. outRect.bottom = marginBottom
      25. } else {
      26. outRect.bottom = builder.divider
      27. }
      28. } else {
      29. outRect.bottom = marginBottom
      30. }
      31. if (lineCount != 0) {
      32. if (lineCount == 1) {
      33. outRect.left = marginHorizontal
      34. outRect.right = marginHorizontal
      35. } else if (parent.getChildLayoutPosition(view) % lineCount == 0) {
      36. // left
      37. outRect.left = marginHorizontal
      38. outRect.right = dividerHorizontal / 2
      39. } else if (parent.getChildLayoutPosition(view) % lineCount == lineCount - 1) {
      40. outRect.left = dividerHorizontal / 2
      41. outRect.right = marginHorizontal
      42. } else {
      43. outRect.left = dividerHorizontal / 2
      44. outRect.right = dividerHorizontal / 2
      45. }
      46. }
      47. }
      48. init {
      49. dividerHorizontal = builder.divider //默认不设置和上下的一致
      50. lineCount = builder.lineCount//一行显示个数
      51. }
      52. class Builder(var divider: Int = 0, var lineCount: Int = 1) {
      53. private val baseItemDecoration = BaseItemDecoration(this)
      54. fun dividerHorizontal(dividerHorizontal: Int): Builder {
      55. baseItemDecoration.dividerHorizontal = dividerHorizontal
      56. return this
      57. }
      58. fun marginTop(marginTop: Int): Builder {
      59. baseItemDecoration.marginTop = marginTop
      60. return this
      61. }
      62. fun marginBottom(marginBottom: Int): Builder {
      63. baseItemDecoration.marginBottom = marginBottom
      64. return this
      65. }
      66. fun marginHorizontal(marginHorizontal: Int): Builder {
      67. baseItemDecoration.marginHorizontal = marginHorizontal
      68. return this
      69. }
      70. fun lineCount(lineCount: Int): Builder {
      71. this.lineCount = lineCount
      72. return this
      73. }
      74. fun divider(divider: Int): Builder {
      75. this.divider = divider
      76. return this
      77. }
      78. fun build() = baseItemDecoration
      79. }
      80. }
  • 2.使用

    1. //使用 BaseItemDecoration 实现同样的效果,初始间距默认 0,一列默认展示一个
    2. val itemDecorationBase = BaseItemDecoration.Builder(space)
    3. .marginTop(space)//顶部间距
    4. .marginBottom(space)//底部间距
    5. .marginHorizontal(space *2)//两侧
    6. .dividerHorizontal(space * 2)//设置每个 item 左右之间的间隔,
    7. .lineCount(1)//设置一列展示几行,和构造函数初始化效果一样
    8. .divider(space * 2)//设置每个 item 上下之间的间隔,和构造函数初始化效果一样
    9. .build()

    二、绘制分割线,可以设置左右间隔

    库中类名修改成下面三个:

    1. new LinearDividerDecoration.Builder();
    2. new GridDividerDecoration.Builder();
    3. new ItemDivider();


    2.1 线性布局,可以设置最后一行是否绘制

    image.png ```kotlin public class LinearItemDecoration extends RecyclerView.ItemDecoration {

    private Drawable mDivider; private boolean mShowLastLine; private int mSpanSpace = 2; private int mLeftPadding; private int mRightPadding;

    public LinearItemDecoration(int span,int leftPadding,int rightPadding,int color,boolean show){

    1. mSpanSpace = span;
    2. mShowLastLine = show;
    3. mLeftPadding = leftPadding;
    4. mRightPadding = rightPadding;
    5. mDivider = new ColorDrawable(color);

    }

    @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

    1. int count = mShowLastLine ? parent.getAdapter().getItemCount() : parent.getAdapter().getItemCount() - 1;
    2. if (isVertical(parent)) {
    3. if (parent.getChildAdapterPosition(view) < count) {
    4. outRect.set(0, 0, 0, mSpanSpace);
    5. } else {
    6. outRect.set(0, 0, 0, 0);
    7. }
    8. } else {
    9. if (parent.getChildAdapterPosition(view) < count) {
    10. outRect.set(0, 0, mSpanSpace, 0);
    11. } else {
    12. outRect.set(0, 0, 0, 0);
    13. }
    14. }

    }

    private boolean isVertical(RecyclerView parent) {

    1. RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    2. if (layoutManager instanceof LinearLayoutManager) {
    3. int orientation = ((LinearLayoutManager) layoutManager) .getOrientation();
    4. return orientation == LinearLayoutManager.VERTICAL;
    5. }
    6. return false;

    }

    @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

    1. if (isVertical(parent)) {
    2. drawVertical(c, parent);
    3. } else {
    4. drawHorizontal(c, parent);
    5. }

    }

    private void drawVertical(Canvas c, RecyclerView parent) {

    1. final int left = parent.getPaddingLeft() + mLeftPadding;
    2. final int right = parent.getWidth() - parent.getPaddingRight() - mRightPadding;
    3. final int childCount = parent.getChildCount();
    4. for (int i = 0; i < childCount; i++) {
    5. final View child = parent.getChildAt(i);
    6. final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams();
    7. final int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
    8. final int bottom = top + mSpanSpace; int count = mShowLastLine ? parent.getAdapter().getItemCount() : parent.getAdapter().getItemCount() - 1;
    9. if (i < count) {
    10. mDivider.setBounds(left, top, right, bottom);
    11. mDivider.draw(c);
    12. } else {
    13. mDivider.setBounds(left, top, right, top);
    14. mDivider.draw(c);
    15. }
    16. }

    }

    private void drawHorizontal(Canvas c, RecyclerView parent) {

    1. final int top = parent.getPaddingTop();
    2. final int bottom = parent.getHeight() - parent.getPaddingBottom();
    3. final int childCount = parent.getChildCount();
    4. for (int i = 0; i < childCount; i++) {
    5. final View child = parent.getChildAt(i);
    6. final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams();
    7. final int left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
    8. final int right = left + mSpanSpace; int count = mShowLastLine ? parent.getAdapter().getItemCount() : parent.getAdapter().getItemCount() - 1;
    9. if (i < count) {
    10. mDivider.setBounds(left, top, right, bottom);
    11. mDivider.draw(c);
    12. }
    13. }

    }

    /**

    • Builder模式
    • */ public static class Builder{

      private Context mContext; private Resources mResources; private int mSpanSpace; private boolean mShowLastLine; private int mLeftPadding; private int mRightPadding; private int mColor;

      public Builder(Context context){

      1. mContext = context;
      2. mResources = context.getResources();
      3. mSpanSpace = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 1f, context.getResources().getDisplayMetrics());
      4. mLeftPadding = 0;
      5. mRightPadding = 0;
      6. mShowLastLine = false;
      7. mColor = Color.BLACK;

      }

      /**

      • 设置分割线宽(高)度 */ public Builder setSpan(float pixels) { mSpanSpace = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pixels, mResources.getDisplayMetrics()); return this;
        }

        /**

      • 设置分割线宽(高)度 */ public Builder setSpan(@DimenRes int resource) { mSpanSpace = mResources.getDimensionPixelSize(resource); return this; }

        /**

      • 设置左右间距 */ public Builder setPadding(float pixels) { setLeftPadding(pixels); setRightPadding(pixels); return this; }

        /**

      • 设置左右间距 */ public Builder setPadding(@DimenRes int resource) { setLeftPadding(resource); setRightPadding(resource); return this; }

        /**

      • 设置左间距 */ public Builder setLeftPadding(float pixelPadding) { mLeftPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pixelPadding, mResources.getDisplayMetrics()); return this; }

        /**

      • 设置右间距 */ public Builder setRightPadding(float pixelPadding) { mRightPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pixelPadding, mResources.getDisplayMetrics()); return this; }

        /**

      • 通过资源id设置左间距 */ public Builder setLeftPadding(@DimenRes int resource) { mLeftPadding = mResources.getDimensionPixelSize(resource); return this; }

        /**

      • 通过资源id设置右间距 */ public Builder setRightPadding(@DimenRes int resource) { mRightPadding = mResources.getDimensionPixelSize(resource); return this; }

        /**

      • 通过资源id设置颜色 */ public Builder setColorResource(@ColorRes int resource) { setColor(ContextCompat.getColor(mContext,resource)); return this; }

        /**

      • 设置颜色 */ public Builder setColor(@ColorInt int color) { mColor = color; return this; }

        /**

      • 是否最后一条显示分割线
      • */ public Builder setShowLastLine(boolean show){ mShowLastLine = show; return this; }

        /**

      • Instantiates a LinearItemDecoration with the specified parameters.
      • @return a properly initialized LinearItemDecoration instance */ public LinearItemDecoration build() { return new LinearItemDecoration(mSpanSpace,mLeftPadding,mRightPadding,mColor,mShowLastLine); }

      }

}

  1. 使用:
  2. ```kotlin
  3. val divider = LinearItemDecoration.Builder(this)
  4. .setSpan(20f)
  5. .setPadding(R.dimen.m10)
  6. // .setLeftPadding(R.dimen.m10)
  7. // .setRightPadding(R.dimen.m10)
  8. .setColorResource(R.color.red)
  9. .setShowLastLine(true)
  10. .build()
  11. //设置自定义分割线
  12. it.addItemDecoration(divider)

2.2 网格布局,可以设置最后一行底部是否绘制,最后一个 item 右侧不会绘制

效果图:
image.png

  1. public class GridItemDecoration extends RecyclerView.ItemDecoration {
  2. private Drawable mDivider;
  3. private boolean mShowLastLine;
  4. private int mHorizonSpan;
  5. private int mVerticalSpan;
  6. private GridItemDecoration(int horizonSpan,int verticalSpan,int color,boolean showLastLine) {
  7. this.mHorizonSpan = horizonSpan;
  8. this.mShowLastLine = showLastLine;
  9. this.mVerticalSpan = verticalSpan;
  10. mDivider = new ColorDrawable(color);
  11. }
  12. @Override
  13. public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
  14. drawHorizontal(c, parent);
  15. drawVertical(c, parent);
  16. }
  17. private void drawHorizontal(Canvas c, RecyclerView parent) {
  18. int childCount = parent.getChildCount();
  19. for (int i = 0; i < childCount; i++) {
  20. View child = parent.getChildAt(i);
  21. //最后一行底部横线不绘制
  22. if (isLastRaw(parent,i,getSpanCount(parent),childCount) && !mShowLastLine){
  23. continue;
  24. }
  25. RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
  26. final int left = child.getLeft() - params.leftMargin;
  27. final int right = child.getRight() + params.rightMargin;
  28. final int top = child.getBottom() + params.bottomMargin;
  29. final int bottom = top + mHorizonSpan;
  30. mDivider.setBounds(left, top, right, bottom);
  31. mDivider.draw(c);
  32. }
  33. }
  34. private void drawVertical(Canvas c, RecyclerView parent) {
  35. int childCount = parent.getChildCount();
  36. for (int i = 0; i < childCount; i++) {
  37. final View child = parent.getChildAt(i);
  38. if((parent.getChildViewHolder(child).getAdapterPosition() + 1) % getSpanCount(parent) == 0){
  39. continue;
  40. }
  41. final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
  42. final int top = child.getTop() - params.topMargin;
  43. final int bottom = child.getBottom() + params.bottomMargin + mHorizonSpan;
  44. final int left = child.getRight() + params.rightMargin;
  45. int right = left + mVerticalSpan;
  46. // //满足条件( 最后一行 && 不绘制 ) 将vertical多出的一部分去掉;
  47. if (i==childCount-1) {
  48. right -= mVerticalSpan;
  49. }
  50. mDivider.setBounds(left, top, right, bottom);
  51. mDivider.draw(c);
  52. }
  53. }
  54. /**
  55. * 计算偏移量
  56. * */
  57. @Override
  58. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  59. int spanCount = getSpanCount(parent);
  60. int childCount = parent.getAdapter().getItemCount();
  61. int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
  62. if (itemPosition < 0){
  63. return;
  64. }
  65. int column = itemPosition % spanCount;
  66. int bottom;
  67. int left = column * mVerticalSpan / spanCount;
  68. int right = mVerticalSpan - (column + 1) * mVerticalSpan / spanCount;
  69. if (isLastRaw(parent, itemPosition, spanCount, childCount)){
  70. if (mShowLastLine){
  71. bottom = mHorizonSpan;
  72. }else{
  73. bottom = 0;
  74. }
  75. }else{
  76. bottom = mHorizonSpan;
  77. }
  78. outRect.set(left, 0, right, bottom);
  79. }
  80. /**
  81. * 获取列数
  82. * */
  83. private int getSpanCount(RecyclerView parent) {
  84. // 列数
  85. int mSpanCount = -1;
  86. RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
  87. if (layoutManager instanceof GridLayoutManager) {
  88. mSpanCount = ((GridLayoutManager) layoutManager).getSpanCount();
  89. } else if (layoutManager instanceof StaggeredGridLayoutManager) {
  90. mSpanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
  91. }
  92. return mSpanCount;
  93. }
  94. /**
  95. * 是否最后一行
  96. * @param parent RecyclerView
  97. * @param pos 当前item的位置
  98. * @param spanCount 每行显示的item个数
  99. * @param childCount child个数
  100. * */
  101. private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) {
  102. RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
  103. if (layoutManager instanceof GridLayoutManager) {
  104. return getResult(pos,spanCount,childCount);
  105. } else if (layoutManager instanceof StaggeredGridLayoutManager) {
  106. int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
  107. if (orientation == StaggeredGridLayoutManager.VERTICAL) {
  108. // StaggeredGridLayoutManager 且纵向滚动
  109. return getResult(pos,spanCount,childCount);
  110. } else {
  111. // StaggeredGridLayoutManager 且横向滚动
  112. if ((pos + 1) % spanCount == 0) {
  113. return true;
  114. }
  115. }
  116. }
  117. return false;
  118. }
  119. private boolean getResult(int pos,int spanCount,int childCount){
  120. int remainCount = childCount % spanCount;//获取余数
  121. //如果正好最后一行完整;
  122. if (remainCount == 0){
  123. if(pos >= childCount - spanCount){
  124. return true; //最后一行全部不绘制;
  125. }
  126. }else{
  127. if (pos >= childCount - childCount % spanCount){
  128. return true;
  129. }
  130. }
  131. return false;
  132. }
  133. /**
  134. * 使用Builder构造
  135. * */
  136. public static class Builder {
  137. private Context mContext;
  138. private Resources mResources;
  139. private boolean mShowLastLine;
  140. private int mHorizonSpan;
  141. private int mVerticalSpan;
  142. private int mColor;
  143. public Builder(Context context) {
  144. mContext = context;
  145. mResources = context.getResources();
  146. mShowLastLine = true;
  147. mHorizonSpan = 0;
  148. mVerticalSpan = 0;
  149. mColor = Color.WHITE;
  150. }
  151. /**
  152. * 通过资源文件设置分隔线颜色
  153. */
  154. public Builder setColorResource(@ColorRes int resource) {
  155. setColor(ContextCompat.getColor(mContext, resource));
  156. return this;
  157. }
  158. /**
  159. * 设置颜色
  160. */
  161. public Builder setColor(@ColorInt int color) {
  162. mColor = color;
  163. return this;
  164. }
  165. /**
  166. * 通过dp设置垂直间距
  167. * */
  168. public Builder setVerticalSpan(@DimenRes int vertical) {
  169. this.mVerticalSpan = mResources.getDimensionPixelSize(vertical);
  170. return this;
  171. }
  172. /**
  173. * 通过px设置垂直间距
  174. * */
  175. public Builder setVerticalSpan(float mVertical) {
  176. this.mVerticalSpan = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mVertical, mResources.getDisplayMetrics());
  177. return this;
  178. }
  179. /**
  180. * 通过dp设置水平间距
  181. * */
  182. public Builder setHorizontalSpan(@DimenRes int horizontal) {
  183. this.mHorizonSpan = mResources.getDimensionPixelSize(horizontal);
  184. return this;
  185. }
  186. /**
  187. * 通过px设置水平间距
  188. * */
  189. public Builder setHorizontalSpan(float horizontal) {
  190. this.mHorizonSpan = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, horizontal, mResources.getDisplayMetrics());
  191. return this;
  192. }
  193. /**
  194. * 是否最后一条显示分割线
  195. * */
  196. public GridItemDecoration.Builder setShowLastLine(boolean show){
  197. mShowLastLine = show;
  198. return this;
  199. }
  200. public GridItemDecoration build() {
  201. return new GridItemDecoration(mHorizonSpan, mVerticalSpan, mColor,mShowLastLine);
  202. }
  203. }
  204. }

使用:

  1. val color = ContextCompat.getColor(this, R.color.red)
  2. val divider = GridItemDecoration.Builder(this)
  3. .setColor(color)
  4. .setHorizontalSpan(R.dimen.m10)
  5. .setVerticalSpan(R.dimen.m10)
  6. .setShowLastLine(false)
  7. .build()
  8. //设置自定义分割线
  9. it.addItemDecoration(divider)

2.3 网格布局分割线,底部自己判断是否绘制,右侧最后一个 item 会绘制,缺点线条粗细不一

效果图:
image.png

  1. public class ItemDivider extends RecyclerView.ItemDecoration {
  2. private int dividerWith = 4;
  3. private Paint paint;
  4. private RecyclerView.LayoutManager layoutManager;
  5. // 构造方法,可以在这里做一些初始化,比如指定画笔颜色什么的
  6. public ItemDivider() {
  7. initPaint();
  8. paint.setColor(0xffE6E6E6);
  9. }
  10. private void initPaint() {
  11. if (paint == null) {
  12. paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  13. paint.setStyle(Paint.Style.FILL);
  14. }
  15. }
  16. public ItemDivider setDividerWith(int dividerWith) {
  17. this.dividerWith = dividerWith;
  18. return this;
  19. }
  20. public ItemDivider setDividerColor(int color) {
  21. initPaint();
  22. paint.setColor(color);
  23. return this;
  24. }
  25. /**
  26. * 指定item之间的间距(就是指定分割线的宽度) 回调顺序 1
  27. * @param outRect Rect to receive the output.
  28. * @param view The child view to decorate
  29. * @param parent RecyclerView this ItemDecoration is decorating
  30. * @param state The current state of RecyclerView.
  31. */
  32. @Override
  33. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  34. super.getItemOffsets(outRect, view, parent, state);
  35. if (layoutManager == null) {
  36. layoutManager = parent.getLayoutManager();
  37. }
  38. // 适用 LinearLayoutManager 和 GridLayoutManager
  39. if (layoutManager instanceof LinearLayoutManager) {
  40. int orientation = ((LinearLayoutManager) layoutManager).getOrientation();
  41. if (orientation == LinearLayoutManager.VERTICAL) {
  42. // 水平分割线将绘制在item底部
  43. outRect.bottom = dividerWith;
  44. } else if (orientation == LinearLayoutManager.HORIZONTAL) {
  45. // 垂直分割线将绘制在item右侧
  46. outRect.right = dividerWith;
  47. }
  48. if (layoutManager instanceof GridLayoutManager) {
  49. GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) view.getLayoutParams();
  50. // 如果是 GridLayoutManager 则需要绘制另一个方向上的分割线
  51. if (orientation == LinearLayoutManager.VERTICAL && lp != null && lp.getSpanIndex() > 0) {
  52. // 如果列表是垂直方向,则最左边的一列略过
  53. outRect.left = dividerWith;
  54. } else if (orientation == LinearLayoutManager.HORIZONTAL && lp != null && lp.getSpanIndex() > 0) {
  55. // 如果列表是水平方向,则最上边的一列略过
  56. outRect.top = dividerWith;
  57. }
  58. }
  59. }
  60. }
  61. /**
  62. * 在item 绘制之前调用(就是绘制在 item 的底层) 回调顺序 2
  63. * 一般分割线在这里绘制
  64. * 看到canvas,对自定义控件有一定了解的话,就能想到为什么说给RecyclerView设置分割线更灵活了
  65. * @param c Canvas to draw into
  66. * @param parent RecyclerView this ItemDecoration is drawing into
  67. * @param state The current state of RecyclerView
  68. */
  69. @Override
  70. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  71. super.onDraw(c, parent, state);
  72. RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
  73. int spanCount = 0;
  74. if (layoutManager instanceof GridLayoutManager){
  75. spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
  76. }
  77. int childCount = parent.getChildCount();
  78. int count = childCount % spanCount;
  79. if (count == 0){
  80. count = spanCount;
  81. }
  82. // 这个值是为了补偿横竖方向上分割线交叉处间隙
  83. int offSet = (int) Math.ceil(dividerWith * 1f / 2);
  84. for (int i = 0; i < parent.getChildCount(); i++) {
  85. View child = parent.getChildAt(i);
  86. RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
  87. int left1 = child.getRight() + params.rightMargin;
  88. int right1 = left1 + dividerWith;
  89. int top1 = child.getTop() - offSet - params.topMargin;
  90. int bottom1 = child.getBottom() + offSet + params.bottomMargin;
  91. //绘制分割线(矩形)
  92. c.drawRect(left1, top1, right1, bottom1, paint);
  93. int left2 = child.getLeft() - offSet - params.leftMargin;
  94. int right2 = child.getRight() + offSet + params.rightMargin;
  95. int top2 = child.getBottom() + params.bottomMargin;
  96. int bottom2 = top2 + dividerWith;
  97. //绘制分割线(矩形)
  98. //c.drawRect(left2, top2, right2, bottom2, paint);
  99. //判断最后一行不画
  100. if (i < parent.getChildCount() -count){
  101. c.drawRect(left2, top2, right2, bottom2, paint);
  102. }
  103. }
  104. }
  105. /**
  106. * 在item 绘制之后调用(就是绘制在 item 的上层) 回调顺序 3
  107. * 也可以在这里绘制分割线,和上面的方法 二选一
  108. * @param c Canvas to draw into
  109. * @param parent RecyclerView this ItemDecoration is drawing into
  110. * @param state The current state of RecyclerView
  111. */
  112. @Override
  113. public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
  114. super.onDrawOver(c, parent, state);
  115. }
  116. }

使用:

  1. //设置自定义分割线
  2. it.addItemDecoration(ItemDivider().setDividerColor(color))

三、地址

demo