Material Design材料设计原则----RecyclerView控件(三)高级进阶之自己绘制列表分隔线

经过RecyclerView的基本使用(一)和(二),我们发现RecyclerView列表项之间默认是没有分隔线的,原先旧的列表控件(Listview)的各列表项之间默认是有分割线的。今天就来总结一下如何为RecyclerView列表添加自定义分割线。

源代码下载:https://download.csdn.net/download/gaoxiaoweiandy/10385456

博客网址:   https://blog.csdn.net/gaoxiaoweiandy/article/details/80157658

效果图如下:



为RecyclerView自定义分割线需要用到一个类

ItemDecoration列表项装饰类,我们就是通过继承这个类来为RecyclerView列表的ITEM定义间隔线的。
ItemDecoration的具体运用如下:
 
 
//实例
for (int i = 0; i < 10; i++) {
   list.add("item"+i);
}
化布局中的 recylerview 控件 recylerview = (RecyclerView)findViewById(R.id. recylerview);
recylerview.addItemDecoration(new MyItemDecoration(this, LinearLayout.VERTICAL));

//普通列表的适配器
adapter = new MyRecyclerAdapter(list);

//列表显示风格为 垂直方向的列表
recylerview.setLayoutManager(new LinearLayoutManager(this));

//装载显示列表数据
recylerview.setAdapter(adapter);
以上代码片段声明了一个RecyclerView控件并且设置了一个adapter,列表要显示的数据在list里。我们主要看这一句
 
 
recylerview.addItemDecoration(new MyItemDecoration(this);
这个就是为RecyclerView列表控件的ITEM列表项添加了一个自定义装饰,其中DividerItemDecoration是我们自定义的类
继承于系统类ItemDecoration,也就是说我们将通过重新定义ItemDecoration类来 自绘制 列表项的间隔线。好,下来我

们就来看看这个MyItemDecoration类里都干了什么?

第一步,新建一个类MyItemDecoration让它继承于ItemDecoration,代码如下:

public class MyItemDecoration extends ItemDecoration {
  
   @Override
   public void onDraw(Canvas c, RecyclerView parent, State state) {

      super.onDraw(c, parent, state);
   }
 
 
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
      State state) {
   
}
在这里我们重写父类的onDraw和getItemOffsets方法。这两个方法有何用,
1. getItemOffsets:
决定了一个绘制的矩形空间Rect,也就是说在这里为 绘制分割线开一个槽子,将来就在这个矩形Rect区域
内任意绘制分割线,且不能超过这个矩形边界。如下图1红色矩形Rect边界

图1

2. onDraw在这里绘制线条,画布的大小也就是上边1中的RECT矩形区域,就在这个区域上绘线。onDraw函数

系统会自动回调这个方法。

知道了以上2个函数的作用后,我们就开始填充代码了

首先我们来看第一个函数getItemOffsets,

在这里我们来定义Rect的大小,添加如下代码:

outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
set函数的4个参数分别是Rect的4个边界: 左、上、右、下。在此假设列表是垂直列表,那么我们应该画的间隔线是
水平的,因此矩形的4个边界分别是:

左边界:x坐标为0(相对于ITEM左边的水平位移)

顶边界: y坐标为0(相对于item底部的垂直偏移量)

以上x,y就是矩形左上角的顶点坐标(相对于ITEM的偏移量,参考坐标系原点为ITEM的左下角

那个点(0,0)。

右边界:x坐标仍为0,这里本应是ITEM的宽度,但是RECT区域会随着ITEM的宽度而拉伸,因此

这里的大小无关紧要,不管赋值为什么值,最终还是ITEM的宽度。

下边界: y坐标为分割线mDivider的高度。(我们恰好能给分割线留够相应的空间即可)。

至此,矩形Rect的左上角顶点坐标(0,0),右下角坐标(0,mDivider.getIntrinsicHeight())都确定下来了
),最后再强调以下2点:(1)这个RECT的坐标系原点为ITEM的左下角,即可以理解为Rect的四个边界就是

相对于ITEM的偏移量。(2)RECT的右边界坐标无需指定值,这里默认为0,因为它会随着ITEM的宽度拉伸。

接下来我们要在onDraw函数里绘制分割线了:

int left = parent.getPaddingLeft();//线的x起点坐标
int right = parent.getWidth() - parent.getPaddingRight();//线的终点x坐标
int childCount = parent.getChildCount();
for (int i = 0; i < childCount ; i++) {
   View child = parent.getChildAt(i);
   
   LayoutParams params = (LayoutParams) child.getLayoutParams();
   int top = child.getBottom() + params.bottomMargin + Math.round  //线的y方向的起点坐标
(ViewCompat.getTranslationY(child));
   int bottom = top + mDivider.getIntrinsicHeight();  //线的y方向的终点坐标
   mDivider.setBounds(left, top , right, bottom);
   mDivider.draw(c);
}
首先left和right是对于各个分割线是固定不变的,各分割线都是从最左边绘制到最右边。这里getPadding考虑了

recyclerView的内边距。然后,我们要绘制每一个分割线,在for循环里计算出每一个分割线的top,

这个top是分割线相对于RecyclerView 顶部的偏移量,bottom也是相对于RecyclerView顶部的偏移量

   View child = parent.getChildAt(i);
   
   LayoutParams params = (LayoutParams) child.getLayoutParams();
   int top = child.getBottom() + params.bottomMargin + 
 Math.round (ViewCompat.getTranslationY(child));
这段代码,获得 每一个child即列表项 的 底部Y坐标 和 child的margin属性,所以线的top y坐标应该是child的y

坐标(child底边Y坐标)再往下一点点(这个一点点就是margin,例如marginBottom=10dp).最终

分割线的top Y坐标应该是child.getBottom() + params.bottomMargin ,至于后边的

getTranslationY(child)是考虑到child有时会有Y方向上偏移的动画效果,如果没有动画效果则 可以忽略
不计。最终 mDivider.setBounds(left, top , right, bottom);确立了线条要绘制在cavas上的矩形边界。
在这里mDivider是一个drawable类型的分割线图片。最后调用mDivider.draw(canvas);将自己绘制在画布的
Bounds设定的区域内 。
   mDivider.setBounds(left, top , right, bottom).

最后贴出DividerItemDecoration类的完整代码:

package com.anyikang.volunteer.sos.recyclerview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ItemDecoration;
import android.support.v7.widget.RecyclerView.LayoutParams;
import android.support.v7.widget.RecyclerView.State;
import android.view.View;

public class MyItemDecorationextends ItemDecoration {
   

   private Drawable mDivider;
   private int[] attrs= new int[]{
         android.R.attr.listDivider
   };
   
   public MyItemDecoration(Context context) {
      TypedArray typedArray = context.obtainStyledAttributes(attrs);
      mDivider = typedArray.getDrawable(0);
      typedArray.recycle();
    
   }


   
   @Override
   public void onDraw(Canvas c, RecyclerView parent, State state) {
    // 画水平线
      int left = parent.getPaddingLeft();
      int right = parent.getWidth() - parent.getPaddingRight();
      int childCount = parent.getChildCount();
      for (int i = 0; i < childCount ; i++) {
         View child = parent.getChildAt(i);
         
         LayoutParams params = (LayoutParams) child.getLayoutParams();
         int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
         int bottom = top + mDivider.getIntrinsicHeight();
         mDivider.setBounds(left, top , right, bottom);
         mDivider.draw(c);
      }
super.onDraw(c, parent, state); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { outRect.set( 0, 0, 0, mDivider.getIntrinsicHeight()); }

源码地址:

https://download.csdn.net/download/gaoxiaoweiandy/10385456 







   

 
 
 
 
 





 

猜你喜欢

转载自blog.csdn.net/gaoxiaoweiandy/article/details/80157658