经过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