神刀安全网

RecyclerView添加自定义ItemDecoration实现(1)

  • 对于RecyclerView的使用想必大家都很熟悉,下面先看两张LinearLayoutManager和GridLayoutManager的示例
RecyclerView添加自定义ItemDecoration实现(1)

LinearLayoutManager_GridLayoutManager.png

  • 上面展示的是没有分割线的示例,一般情况下我们是通过继承RecyclerView.ItemDecoration类并重写 getItemOffsets 与 onDraw 来实现自定义分割线。

代码:

 public class CommonDecoration extends RecyclerView.ItemDecoration {      public CommonDecoration() {       }          /**      * @param outRect 用于规定分割线的范围      * @param view    进行分割线操作的子view      * @param parent  父view      * @param state   (这里暂时不使用)      */     @Override     public void getItemOffsets(Rect outRect, View view, RecyclerView parent,    RecyclerView.State state) {         super.getItemOffsets(outRect, view, parent, state);     }       @Override      public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {         super.onDraw(c, parent, state);     }      } 

SquareRelativelayout.java

public class SquareRelativelayout extends RelativeLayout {     public SquareRelativelayout(Context context) {         super(context);     }      public SquareRelativelayout(Context context, AttributeSet attrs) {         super(context, attrs);     }      public SquareRelativelayout(Context context, AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);     }      @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         super.onMeasure(widthMeasureSpec, heightMeasureSpec);         int size = MeasureSpec.getSize(widthMeasureSpec);         setMeasuredDimension(size, size);     } } 

SquareTextView.java

public class SquareTextView extends AppCompatTextView {     public SquareTextView(Context context) {         super(context);     }      public SquareTextView(Context context, @Nullable AttributeSet attrs) {         super(context, attrs);     }      public SquareTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);     }      @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         super.onMeasure(widthMeasureSpec, heightMeasureSpec);         int size = MeasureSpec.getSize(widthMeasureSpec);         setMeasuredDimension(size, size);     } } 

item_grid.xml

<?xml version="1.0" encoding="utf-8"?> <com.zzz.recyclerviewdemo.view.SquareRelativelayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:background="@android:color/holo_red_light">  <com.zzz.recyclerviewdemo.view.SquareTextView     android:id="@+id/tv_letter"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:background="@android:color/holo_red_light"     android:gravity="center"     android:text="A"     android:textColor="@android:color/white"     android:textSize="20dp" /> </com.zzz.recyclerviewdemo.view.SquareRelativelayout> 

item_linear.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:orientation="vertical">  <TextView     android:id="@+id/tv_letter"     android:layout_width="match_parent"     android:layout_height="60dp"     android:background="@android:color/holo_red_light"     android:gravity="center"     android:text="A"     android:textColor="@android:color/white"     android:textSize="20dp" /> </LinearLayout> 

分割线使用:

mRecyclerView.addItemDecoration(new CommonDecoration(this, R.drawable.drawable_item_decoration)); 

LinearLayoutManager实现分割线

drawable_itemdecoration.xml:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!--用于设置分割线的宽-->     <size android:width="5dp" />     <solid android:color="@android:color/holo_orange_dark" /> </shape> 

代码:

public class CommonDecoration extends RecyclerView.ItemDecoration {      private Context mContext;     private Drawable mDrawable;          public CommonDecoration(Context context, int drawableId) {         this.mContext = context;         this.mDrawable = ContextCompat.getDrawable(this.mContext, drawableId);     }          /**      * @param outRect 用于规定分割线的范围      * @param view    进行分割线操作的子view      * @param parent  父view      * @param state   (这里暂时不使用)      */     @Override     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {         outRect.bottom = mDrawable.getIntrinsicHeight();     }          @Override     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {         for (int i = 0; i < parent.getChildCount(); i++) {             drawHorizontalDecoration(c, parent.getChildAt(i));         }     }          private void drawHorizontalDecoration(Canvas canvas, View childView) {         Rect rect = new Rect(0, 0, 0, 0);         rect.top = childView.getBottom();         rect.bottom = rect.top + mDrawable.getIntrinsicHeight();         rect.left = childView.getLeft();         rect.right = childView.getRight();         mDrawable.setBounds(rect);         mDrawable.draw(canvas);     } } 
  • 简单的分割线的截图
RecyclerView添加自定义ItemDecoration实现(1)

linearLayoutManager_without_isLastRow.png

问题:很明显可以看出在最后一个Item(“Z”)也出现了分割线,即对于显示LinearLayoutManager的最后一个子view的时候不应该存在分割线。

解决:在getItemOffsets方法中进行判断是否为最后一行,使用(currentItemPosition + 1) 与 totalItems比较(currentItemPosition从0开始计数)。如果是最后一行就不给其留出空间,即让outRect.bottom = 0。


代码:

public class CommonDecoration extends RecyclerView.ItemDecoration {      private Context mContext;     private Drawable mDrawable;     //     private int mTotalItems;//总Item数      public CommonDecoration(Context context, int drawableId) {         this.mContext = context;         this.mDrawable = ContextCompat.getDrawable(this.mContext, drawableId);     }      /**      * @param outRect 用于规定分割线的范围      * @param view    进行分割线操作的子view      * @param parent  父view      * @param state   (这里暂时不使用)      */     @Override     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {         if (0 == mTotalItems)             mTotalItems = parent.getAdapter().getItemCount();         //在源码中有一个过时的方法,里面有获取当前ItemPosition         int currentItemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();         if (!isLastRow(currentItemPosition, mTotalItems))             outRect.bottom = mDrawable.getIntrinsicWidth();         else             outRect.bottom = 0;     }      @Override     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {         for (int i = 0; i < parent.getChildCount(); i++) {             drawHorizontalDecoration(c, parent.getChildAt(i));         }     }      private void drawHorizontalDecoration(Canvas canvas, View childView) {         Rect rect = new Rect(0, 0, 0, 0);         rect.top = childView.getBottom();         rect.bottom = rect.top + mDrawable.getIntrinsicWidth();         rect.left = childView.getLeft();         rect.right = childView.getRight();         mDrawable.setBounds(rect);         mDrawable.draw(canvas);     }      private boolean isLastRow(int currentItemPosition, int totalItems) {         boolean result = false;         if (currentItemPosition + 1 >= totalItems)             result = true;         return result;     } } 
  • 解决问题后的截图
RecyclerView添加自定义ItemDecoration实现(1)

linearLayoutManager_with_isLastRow.png

问题:对比两张图可以看出如果子view的数量充满整个屏幕的话是可以不显示分割线的,但是如果子view的数量无法充满整个屏幕的话,分割线又出现了。

解决:上一步我们只更改了getItemOffsets方法中如果是最后一行不给出画分割线的空间,但是如果子view的数量不够的话还是存在画分割线的空间。所以我们还要修改drawHorizontalDecoration方法 ,在该方法中同样判断是否为最后一行。


代码:

 private void drawHorizontalDecoration(Canvas canvas, View childView) {     int currentItemPosition = ((RecyclerView.LayoutParams) childView.getLayoutParams()).getViewLayoutPosition();     if (isLastRow(currentItemPosition, mTotalItems)) {         return;     }     //     Rect rect = new Rect(0, 0, 0, 0);     rect.top = childView.getBottom();     rect.bottom = rect.top + mDrawable.getIntrinsicWidth();     rect.left = childView.getLeft();     rect.right = childView.getRight();     mDrawable.setBounds(rect);     mDrawable.draw(canvas); } 
  • 解决问题后的截图
RecyclerView添加自定义ItemDecoration实现(1)

linearLayoutManager_with_isLastRow_plus.png

GridLayoutManager实现分割线

  • 使用LinearLayoutManager的Decoration截图
RecyclerView添加自定义ItemDecoration实现(1)

GridLayoutManager_use_LinearLayoutManager_decoration.png

问题:可以看出使用了LinearLayoutManager的话只有view的下部分存在分割线,而且它最后一行存在一些问题,我们只去掉了最后一个子view的分割线,而不是一行的分割线。

解决:

  1. 最后一行分割线的问题,所以需要更改 isLastRow 方法。从下示例图中可以看出最后一行跟RecyclerView的列数有关。所以只要判断(currentItemPosition + 1) > ((行数 – 1) x 列数)即可。这样也不会对LinearLayoutManger产生影响。
  2. 因为除了View在下部要有分割线,在子view右部也要有,所以必须设置outRect.right,为右部留出画分割线的空间。
RecyclerView添加自定义ItemDecoration实现(1)

statement.png


代码:

public class CommonDecoration extends RecyclerView.ItemDecoration {      private Context mContext;     private Drawable mDrawable;     //     private int mTotalItems;//总Item数     private int mSpanCount;//总列数      public CommonDecoration(Context context, int drawableId) {         this.mContext = context;         this.mDrawable = ContextCompat.getDrawable(this.mContext, drawableId);     }      /**      * @param outRect 用于规定分割线的范围      * @param view    进行分割线操作的子view      * @param parent  父view      * @param state   (这里暂时不使用)      */     @Override     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {         if (0 == mTotalItems)             mTotalItems = parent.getAdapter().getItemCount();         if (0 == mSpanCount) {             RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();             //判断是否为GridLayoutManager             if (layoutManager instanceof GridLayoutManager) {                 GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;                 mSpanCount = gridLayoutManager.getSpanCount();             } else {                 mSpanCount = 1;             }         }         //在源码中有一个过时的方法,里面有获取当前ItemPosition         int currentItemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();         //         if (!isLastRow(currentItemPosition, mTotalItems, mSpanCount))             outRect.bottom = mDrawable.getIntrinsicWidth();         else             outRect.bottom = 0;         //         outRect.right = mDrawable.getIntrinsicWidth();     }      @Override     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {         for (int i = 0; i < parent.getChildCount(); i++) {             drawHorizontalDecoration(c, parent.getChildAt(i));             drawVerticalDecoration(c, parent.getChildAt(i));         }     }      private void drawHorizontalDecoration(Canvas canvas, View childView) {         int currentItemPosition = ((RecyclerView.LayoutParams) childView.getLayoutParams()).getViewLayoutPosition();         if (isLastRow(currentItemPosition, mTotalItems, mSpanCount)) {                return;         }         //         Rect rect = new Rect(0, 0, 0, 0);          rect.top = childView.getBottom();         rect.bottom = rect.top + mDrawable.getIntrinsicWidth();         rect.left = childView.getLeft();         rect.right = childView.getRight();          mDrawable.setBounds(rect);         mDrawable.draw(canvas);     }      private void drawVerticalDecoration(Canvas canvas, View childView) {         Rect rect = new Rect(0, 0, 0, 0);          rect.top = childView.getTop();         rect.bottom = childView.getBottom();         rect.left = childView.getRight();         rect.right = rect.left + mDrawable.getIntrinsicWidth();          mDrawable.setBounds(rect);         mDrawable.draw(canvas);     }      private boolean isLastRow(int currentItemPosition, int totalItems, int spanCount) {         boolean result = false;         int rowCount = 0;          if (0 == totalItems % spanCount) {             rowCount = totalItems / spanCount;         } else {             rowCount = totalItems / spanCount + 1;         }         if ((currentItemPosition + 1) > (rowCount - 1) * spanCount)             result = true;          return result;     } } 
  • 解决问题后截图
RecyclerView添加自定义ItemDecoration实现(1)

GridLayoutManager_use_LinearLayoutManager_solve.png

问题: 从图中可以看出在水平与垂直方向相接处的分割线没有颜色填充;并且最后一列的的子view不应该有垂直方向的分割线。

解决:

  1. 在 drawHorizontalDecoration 方法中的 rect.right 添加上分割线的宽度。
  2. 添加一个方法判断是否为最后一列使用(currentItemPosition + 1) % 3是否等于0来判断,如果是最后一列的话,则不留出画垂直方向分割线的空间。

代码:

public class CommonDecoration extends RecyclerView.ItemDecoration {      private Context mContext;     private Drawable mDrawable;     //     private int mTotalItems;//总Item数     private int mSpanCount;//总列数      public CommonDecoration(Context context, int drawableId) {         this.mContext = context;         this.mDrawable = ContextCompat.getDrawable(this.mContext, drawableId);     }      /**      * @param outRect 用于规定分割线的范围      * @param view    进行分割线操作的子view      * @param parent  父view      * @param state   (这里暂时不使用)      */     @Override     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {         if (0 == mTotalItems)             mTotalItems = parent.getAdapter().getItemCount();         if (0 == mSpanCount) {             RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();             //判断是否为GridLayoutManager             if (layoutManager instanceof GridLayoutManager) {                 GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;                 mSpanCount = gridLayoutManager.getSpanCount();             } else {                 mSpanCount = 1;             }         }         //在源码中有一个过时的方法,里面有获取当前ItemPosition         int currentItemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();         //         if (!isLastRow(currentItemPosition, mTotalItems, mSpanCount))             outRect.bottom = mDrawable.getIntrinsicWidth();         else             outRect.bottom = 0;         //         if (!isLastColumn(currentItemPosition, mSpanCount)) {             outRect.right = mDrawable.getIntrinsicWidth();         } else {             outRect.right = 0;         }      }      @Override     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {         for (int i = 0; i < parent.getChildCount(); i++) {             drawHorizontalDecoration(c, parent.getChildAt(i));             drawVerticalDecoration(c, parent.getChildAt(i));         }     }      private void drawHorizontalDecoration(Canvas canvas, View childView) {         int currentItemPosition = ((RecyclerView.LayoutParams) childView.getLayoutParams()).getViewLayoutPosition();         if (isLastRow(currentItemPosition, mTotalItems, mSpanCount)) {             return;         }         //         Rect rect = new Rect(0, 0, 0, 0);          rect.top = childView.getBottom();         rect.bottom = rect.top + mDrawable.getIntrinsicWidth();         rect.left = childView.getLeft();         rect.right = childView.getRight() + mDrawable.getIntrinsicWidth();          mDrawable.setBounds(rect);         mDrawable.draw(canvas);     }      private void drawVerticalDecoration(Canvas canvas, View childView) {         Rect rect = new Rect(0, 0, 0, 0);          rect.top = childView.getTop();         rect.bottom = childView.getBottom();         rect.left = childView.getRight();         rect.right = rect.left + mDrawable.getIntrinsicWidth();          mDrawable.setBounds(rect);         mDrawable.draw(canvas);     }      private boolean isLastRow(int currentItemPosition, int totalItems, int spanCount) {         boolean result = false;         int rowCount = 0;          if (0 == totalItems % spanCount) {             rowCount = totalItems / spanCount;         } else {             rowCount = totalItems / spanCount + 1;         }         if ((currentItemPosition + 1) > (rowCount - 1) * spanCount)             result = true;          return result;     }      private boolean isLastColumn(int currentItemPosition, int spanCount) {         boolean result = false;         if (0 == (currentItemPosition + 1) % spanCount)             result = true;         return result;     } } 

解决问题后截图:

RecyclerView添加自定义ItemDecoration实现(1)

final.png

下一次将会从源码的角度,进一步分析当前存在的BUG。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » RecyclerView添加自定义ItemDecoration实现(1)

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址