小言_互联网的博客

超简单!一句话为RecyclerView添加分割线

569人阅读  评论(0)

在使用RecyclerView组件加载数据列表时,产品和美工为了美观都会在每一个item项之间添加分割线,分割线的实现方式一般分为两种,第一种是在布局文件中通过添加<View ...></View>组件,然后在代码中通过方法setVisibility()设置其是否可见,实现分割线效果。

下面一种是通过装饰器为RecyclerView列表的item添加分割线,代码很简单:


  
  1. //找到RecyclerView组件
  2. RecyclerView mCommonRvList = rootView.findViewById(R.id.common_recyclerview_list);
  3. //添加装饰器
  4. mCommonRvList.addItemDecoration( new MyDividerItemDecoration(getContext(),DividerItemDecoration.VERTICAL));

其中MyDividerItemDecoration类是对Android.support.v7.widget包源码类DividerItemDecoration.java一份拷贝后的稍作改动,源码如下:


  
  1. package android.support.v7.widget;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Canvas;
  5. import android.graphics.Rect;
  6. import android.graphics.drawable.Drawable;
  7. import android.support.annotation.NonNull;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.widget.LinearLayout;
  11. /**
  12. * DividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
  13. * between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
  14. * {@link #VERTICAL} orientations.
  15. *
  16. * <pre>
  17. * mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
  18. * mLayoutManager.getOrientation());
  19. * recyclerView.addItemDecoration(mDividerItemDecoration);
  20. * </pre>
  21. */
  22. public class DividerItemDecoration extends RecyclerView.ItemDecoration {
  23. public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
  24. public static final int VERTICAL = LinearLayout.VERTICAL;
  25. private static final String TAG = "DividerItem";
  26. private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };
  27. private Drawable mDivider;
  28. /**
  29. * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
  30. */
  31. private int mOrientation;
  32. private final Rect mBounds = new Rect();
  33. /**
  34. * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
  35. * {@link LinearLayoutManager}.
  36. *
  37. * @param context Current context, it will be used to access resources.
  38. * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
  39. */
  40. public DividerItemDecoration(Context context, int orientation) {
  41. final TypedArray a = context.obtainStyledAttributes(ATTRS);
  42. mDivider = a.getDrawable( 0);
  43. if (mDivider == null) {
  44. Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
  45. + "DividerItemDecoration. Please set that attribute all call setDrawable()");
  46. }
  47. a.recycle();
  48. setOrientation(orientation);
  49. }
  50. /**
  51. * Sets the orientation for this divider. This should be called if
  52. * {@link RecyclerView.LayoutManager} changes orientation.
  53. *
  54. * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
  55. */
  56. public void setOrientation(int orientation) {
  57. if (orientation != HORIZONTAL && orientation != VERTICAL) {
  58. throw new IllegalArgumentException(
  59. "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
  60. }
  61. mOrientation = orientation;
  62. }
  63. /**
  64. * Sets the {@link Drawable} for this divider.
  65. *
  66. * @param drawable Drawable that should be used as a divider.
  67. */
  68. public void setDrawable(@NonNull Drawable drawable) {
  69. if (drawable == null) {
  70. throw new IllegalArgumentException( "Drawable cannot be null.");
  71. }
  72. mDivider = drawable;
  73. }
  74. @Override
  75. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  76. if (parent.getLayoutManager() == null || mDivider == null) {
  77. return;
  78. }
  79. if (mOrientation == VERTICAL) {
  80. drawVertical(c, parent);
  81. } else {
  82. drawHorizontal(c, parent);
  83. }
  84. }
  85. private void drawVertical(Canvas canvas, RecyclerView parent) {
  86. canvas.save();
  87. final int left;
  88. final int right;
  89. //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
  90. if (parent.getClipToPadding()) {
  91. left = parent.getPaddingLeft();
  92. right = parent.getWidth() - parent.getPaddingRight();
  93. canvas.clipRect(left, parent.getPaddingTop(), right,
  94. parent.getHeight() - parent.getPaddingBottom());
  95. } else {
  96. left = 0;
  97. right = parent.getWidth();
  98. }
  99. final int childCount = parent.getChildCount();
  100. for ( int i = 0; i < childCount; i++) {
  101. final View child = parent.getChildAt(i);
  102. parent.getDecoratedBoundsWithMargins(child, mBounds);
  103. final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
  104. final int top = bottom - mDivider.getIntrinsicHeight();
  105. mDivider.setBounds(left, top, right, bottom);
  106. mDivider.draw(canvas);
  107. }
  108. canvas.restore();
  109. }
  110. private void drawHorizontal(Canvas canvas, RecyclerView parent) {
  111. canvas.save();
  112. final int top;
  113. final int bottom;
  114. //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
  115. if (parent.getClipToPadding()) {
  116. top = parent.getPaddingTop();
  117. bottom = parent.getHeight() - parent.getPaddingBottom();
  118. canvas.clipRect(parent.getPaddingLeft(), top,
  119. parent.getWidth() - parent.getPaddingRight(), bottom);
  120. } else {
  121. top = 0;
  122. bottom = parent.getHeight();
  123. }
  124. final int childCount = parent.getChildCount();
  125. for ( int i = 0; i < childCount; i++) {
  126. final View child = parent.getChildAt(i);
  127. parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
  128. final int right = mBounds.right + Math.round(child.getTranslationX());
  129. final int left = right - mDivider.getIntrinsicWidth();
  130. mDivider.setBounds(left, top, right, bottom);
  131. mDivider.draw(canvas);
  132. }
  133. canvas.restore();
  134. }
  135. @Override
  136. public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
  137. RecyclerView.State state) {
  138. if (mDivider == null) {
  139. outRect.set( 0, 0, 0, 0);
  140. return;
  141. }
  142. if (mOrientation == VERTICAL) {
  143. outRect.set( 0, 0, 0, mDivider.getIntrinsicHeight());
  144. } else {
  145. outRect.set( 0, 0, mDivider.getIntrinsicWidth(), 0);
  146. }
  147. }
  148. }

注意其中,RecyclerView列表在设置布局管理器时,使用的是线性布局管理器LinearLayoutManager。


  
  1. private void drawVertical(Canvas canvas, RecyclerView parent) {
  2. canvas.save();
  3. final int left;
  4. final int right;
  5. //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
  6. if (parent.getClipToPadding()) {
  7. //注意这里,分割线绘制的宽度是否填充整个屏幕的宽度是跟RecyclerView组件所在parent父布局是否设置paddingLeft和paddingRight相关的
  8. left = parent.getPaddingLeft();
  9. right = parent.getWidth() - parent.getPaddingRight();
  10. canvas.clipRect(left, parent.getPaddingTop(), right,
  11. parent.getHeight() - parent.getPaddingBottom());
  12. } else {
  13. left = 0;
  14. right = parent.getWidth();
  15. }
  16. final int childCount = parent.getChildCount();
  17. for ( int i = 0; i < childCount; i++) {
  18. final View child = parent.getChildAt(i);
  19. parent.getDecoratedBoundsWithMargins(child, mBounds);
  20. final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
  21. final int top = bottom - mDivider.getIntrinsicHeight();
  22. mDivider.setBounds(left, top, right, bottom);
  23. mDivider.draw(canvas);
  24. }
  25. canvas.restore();
  26. }

上面方法的注释处:分割线绘制的宽度是否填充整个屏幕的宽度是跟RecyclerView组件所在parent父布局是否设置paddingLeft和paddingRight相关的,如果我们不想让分割线整个填充屏幕的宽度,就需要设置RecyclerView组件所在父布局parent的padding值,如下添加 android:paddingRight="10dp"和android:paddingLeft="10dp":


  
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width= "fill_parent"
  4. android:layout_height= "wrap_content"
  5. android:layout_marginTop= "10dp"
  6. android:layout_marginLeft= "5dp"
  7. android:layout_marginRight= "5dp"
  8. android:paddingRight= "10dp"
  9. android:paddingLeft= "10dp"
  10. android:orientation= "vertical">
  11. <android.support.v7.widget.RecyclerView
  12. android:id= "@+id/common_recyclerview_list"
  13. android:layout_width= "match_parent"
  14. android:layout_height= "wrap_content"
  15. />
  16. </LinearLayout>

这个android:paddingRight="10dp"和android:paddingLeft="10dp"值的大小不宜设置过大,否则将影响整个列表内边距的样式,但是设置这个值的绘制后分割线绘制的宽度填充的还是太宽,于是就想办法把上面的源码类DividerItemDecoration.java重新继承一下,修改画分割线宽度的代码:


  
  1. private void drawVertical(Canvas canvas, RecyclerView parent) {
  2. canvas.save();
  3. final int left;
  4. final int right;
  5. //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
  6. if (parent.getClipToPadding()) {
  7. //注意这里,分割线绘制的宽度是否填充整个屏幕的宽度是跟RecyclerView组件所在parent父布局是否设置paddingLeft和paddingRight相关的
  8. left = parent.getPaddingLeft()+ 18;
  9. right = parent.getWidth() - parent.getPaddingRight()- 18;
  10. canvas.clipRect(left, parent.getPaddingTop(), right,
  11. parent.getHeight() - parent.getPaddingBottom());
  12. } else {
  13. left = 0;
  14. right = parent.getWidth();
  15. }
  16. final int childCount = parent.getChildCount();
  17. for ( int i = 0; i < childCount; i++) {
  18. final View child = parent.getChildAt(i);
  19. parent.getDecoratedBoundsWithMargins(child, mBounds);
  20. final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
  21. final int top = bottom - mDivider.getIntrinsicHeight();
  22. mDivider.setBounds(left, top, right, bottom);
  23. mDivider.draw(canvas);
  24. }
  25. canvas.restore();
  26. }

整个代码如下:


  
  1. package com.jsmcc.ui.myaccount.view;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Canvas;
  5. import android.graphics.Rect;
  6. import android.graphics.drawable.Drawable;
  7. import android.support.annotation.NonNull;
  8. import android.support.v7.widget.LinearLayoutManager;
  9. import android.support.v7.widget.RecyclerView;
  10. import android.util.Log;
  11. import android.view.View;
  12. import android.widget.LinearLayout;
  13. import com.jsmcc.utils.DensityUtil;
  14. /**
  15. * MyDividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
  16. * between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
  17. * {@link #VERTICAL} orientations.
  18. *
  19. * <pre>
  20. * mDividerItemDecoration = new MyDividerItemDecoration(recyclerView.getContext(),
  21. * mLayoutManager.getOrientation());
  22. * recyclerView.addItemDecoration(mDividerItemDecoration);
  23. * </pre>
  24. */
  25. public class MyDividerItemDecoration extends RecyclerView.ItemDecoration {
  26. public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
  27. public static final int VERTICAL = LinearLayout.VERTICAL;
  28. private static final String TAG = "DividerItem";
  29. private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };
  30. private Drawable mDivider;
  31. private Context mContext;
  32. /**
  33. * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
  34. */
  35. private int mOrientation;
  36. private final Rect mBounds = new Rect();
  37. /**
  38. * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
  39. * {@link LinearLayoutManager}.
  40. *
  41. * @param context Current context, it will be used to access resources.
  42. * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
  43. */
  44. public MyDividerItemDecoration(Context context, int orientation) {
  45. final TypedArray a = context.obtainStyledAttributes(ATTRS);
  46. mDivider = a.getDrawable( 0);
  47. if (mDivider == null) {
  48. Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
  49. + "MyDividerItemDecoration. Please set that attribute all call setDrawable()");
  50. }
  51. a.recycle();
  52. setOrientation(orientation);
  53. }
  54. /**
  55. * Sets the orientation for this divider. This should be called if
  56. * {@link RecyclerView.LayoutManager} changes orientation.
  57. *
  58. * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
  59. */
  60. public void setOrientation(int orientation) {
  61. if (orientation != HORIZONTAL && orientation != VERTICAL) {
  62. throw new IllegalArgumentException(
  63. "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
  64. }
  65. mOrientation = orientation;
  66. }
  67. /**
  68. * Sets the {@link Drawable} for this divider.
  69. *
  70. * @param drawable Drawable that should be used as a divider.
  71. */
  72. public void setDrawable(@NonNull Drawable drawable) {
  73. if (drawable == null) {
  74. throw new IllegalArgumentException( "Drawable cannot be null.");
  75. }
  76. mDivider = drawable;
  77. }
  78. @Override
  79. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  80. if (parent.getLayoutManager() == null || mDivider == null) {
  81. return;
  82. }
  83. if (mOrientation == VERTICAL) {
  84. drawVertical(c, parent);
  85. } else {
  86. drawHorizontal(c, parent);
  87. }
  88. }
  89. private void drawVertical(Canvas canvas, RecyclerView parent) {
  90. canvas.save();
  91. final int left;
  92. final int right;
  93. //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
  94. if (parent.getClipToPadding()) {
  95. left = parent.getPaddingLeft()+ 18;
  96. right = parent.getWidth() - parent.getPaddingRight()- 18;
  97. canvas.clipRect(left, parent.getPaddingTop(), right,
  98. parent.getHeight() - parent.getPaddingBottom());
  99. } else {
  100. left = 0;
  101. right = parent.getWidth();
  102. }
  103. final int childCount = parent.getChildCount();
  104. for ( int i = 0; i < childCount; i++) {
  105. final View child = parent.getChildAt(i);
  106. parent.getDecoratedBoundsWithMargins(child, mBounds);
  107. final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
  108. final int top = bottom - mDivider.getIntrinsicHeight();
  109. mDivider.setBounds(left, top, right, bottom);
  110. mDivider.draw(canvas);
  111. }
  112. canvas.restore();
  113. }
  114. private void drawHorizontal(Canvas canvas, RecyclerView parent) {
  115. canvas.save();
  116. final int top;
  117. final int bottom;
  118. //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
  119. if (parent.getClipToPadding()) {
  120. top = parent.getPaddingTop();
  121. bottom = parent.getHeight() - parent.getPaddingBottom();
  122. canvas.clipRect(parent.getPaddingLeft(), top,
  123. parent.getWidth() - parent.getPaddingRight(), bottom);
  124. } else {
  125. top = 0;
  126. bottom = parent.getHeight();
  127. }
  128. final int childCount = parent.getChildCount();
  129. for ( int i = 0; i < childCount; i++) {
  130. final View child = parent.getChildAt(i);
  131. parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
  132. final int right = mBounds.right + Math.round(child.getTranslationX());
  133. final int left = right - mDivider.getIntrinsicWidth();
  134. mDivider.setBounds(left, top, right, bottom);
  135. mDivider.draw(canvas);
  136. }
  137. canvas.restore();
  138. }
  139. @Override
  140. public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
  141. RecyclerView.State state) {
  142. if (mDivider == null) {
  143. outRect.set( 0, 0, 0, 0);
  144. return;
  145. }
  146. if (mOrientation == VERTICAL) {
  147. outRect.set( 0, 0, 0, mDivider.getIntrinsicHeight());
  148. } else {
  149. outRect.set( 0, 0, mDivider.getIntrinsicWidth(), 0);
  150. }
  151. }
  152. }

运行代码,这样成功的实现了产品要求的效果!

总结:

① 为列表组件添加装饰器:

//添加装饰器
mCommonRvList.addItemDecoration(new MyDividerItemDecoration(getContext(),DividerItemDecoration.VERTICAL));

② 为列表组件的父布局添加padding,如上;

③ 将MyDividerItemDecoration.java 拷贝到你的项目里,在①处new 创建就可以了。

如有更好的方法欢迎留言指正!不胜感激!

推荐一篇很好的文章:https://blog.csdn.net/lmj623565791/article/details/45059587

 


转载:https://blog.csdn.net/luqingshuai_eloong/article/details/106192741
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场