作者:Lipt0n
地址:http://www.jianshu.com/p/ce78da3e0ff6
声明:本文是Lipt0n原创,已获其授权发布,未经原作者允许请勿转载
RecyclerView性能和自由度相比ListView强大很多,但很恼人的是它没有像ListView一样默认提供分割线。
刚接触RecyclerView,用过才发现RecyclerView没有分割线过后,遂到网上搜解决办法才发现自定义一个ItemDecoration只要一条黑线还要写代码,好麻烦,不知道有没像我一样懒得折腾上网搜现成的,粘贴到项目直接用。
拖了很久才去解决这个问题,上网大致看了一下教程,其实不难而且自定义功能很强大。
首先新建一个类覆写ItemDecoration里面有三个方法:
public class SimpleItemDecoration extends RecyclerView.ItemDecoration {
public SimpleItemDecoration(Context context) {
}
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
}
onDraw名字很熟悉吧,和View中的onDraw一样,是用来画东西的, 在item上画分割线就靠这个方法了.
onDrawOver 英文Over的意思在…的上面 ,可以理解成是图层关系,item的内容和分割线是第一层(要在第一层画东西要调用onDraw),而onDrawOver是第二层,位于onDraw的上面getItemOffsets看名字可以知道是设置item的偏移值,其实效果和padding一样。以上三个方法都是在RecylerView发生滑动的时候触发。
需要注意的是三个方法的都有一个RecyclerView parent,通过这个参数我们可以获取到RecyclerView的属性,例如 parent.getChildCount();获取子View的个数,但是这个并不是获取RecyclerView所有的item个数,而是当前屏幕可见的item个数。
所以画一条分割线需要的代码是这样的:
private int wight;
private int height;
private int item_height;
private int item_padding;
private Paint paint;
public SimpleItemDecoration(Context context) {
wight=context.getResources().getDisplayMetrics().widthPixels;
height=context.getResources().getDisplayMetrics().heightPixels;
paint=new Paint(Paint.ANTI_ALIAS_FLAG|Paint.DITHER_FLAG);
paint.setColor(Color.BLACK);
item_height=DensityUtil.dip2px(context, 1);
item_padding=DensityUtil.dip2px(context, 10);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int count=parent.getChildCount();
for (int i = 0; i < count; i++) {
View view=parent.getChildAt(i);
int top=view.getTop();
int bottom=top+item_height;
c.drawRect(0,top,wight,bottom,paint);
}
.....
}
运行后得到如下图的效果。
接着把 item_height=DensityUtil.dip2px(context, 1);
的1改成30,你会发现item的内容和黑色的分割线重合了(黑人问号脸???)
因为上面说了item和内容和onDraw中画的内容在同一图层,当然会被出现重合的情况.这个时候getItemOffsets就能派上用场了.只要在原来的item的加个偏移值(效果和在Adpater中为item设置padding的效果是一样的,只是在ItemDecoration统一处理比较合适)onDraw中画的分割线有多高,我就paddingBottom多少.
所以代码是是这样的:
再次运行代码item被挡住的问题就解决了,RecyclerView的自定义ItemDecoration就是这么简单.有点自定义View经验的人理解起来都不会难
分割线不要占满,要有和Left,Right有间距啊?
添加如下代码:
一般用到的分割线根据以上的代码再自己的按照需求稍微修改一下基本都能满足需求了.
之前在网上看到通过自定义ItemDecoration实现仿照旧版的instagram吸顶效果,感觉那种效果很好看,研究了一下发现只要理解了上面文章所说的几个方法实现起来并不难.
先来看最终效果图:
要实现吸顶的效果需要完成这些步骤:
先来实现1和2的要求,主要代码部分如下:
注意看陈奕迅这个item的分割线是在item的上面的,并且分割线的高度已经足够容下我们稍后要绘制的内容了.
接着来实现3,怎么样才能让分割线在满足条件的时候动,不满足的时候固定?
这个时候就需要用到代码中一直没覆写的onDrawOver方法了,先来实现固定不动的分割线,代码也是非常的简单,在原来的代码上覆写onDrawOver方法(这里new了新的画笔paint2,把固定的分割线用半透明红色来作为背景,方便理解效果):
接着来实现实现: 当前屏幕可见的第一个item的Bottom<=item_height(分割线的高度)让第一条分割线随着RecyclerView向上滑动直到滑出屏幕,代码如下:
可以看到滑动时当第二item的顶部和第一个item的底部相互接触到后继续滑动的话第一个item就会慢慢向上滑动,直到第一个item完全画出屏幕,固定分割线立马回到最开始的位置和item2分割线重叠了在一起,现在可以把paint2换回paint效果会更直观,不上效果图了,可以自己去测试.
对第一次接触ItemDecoration的人来说,难点都已经讲完了,剩下的就是在分割线范围计算出合适的位置调动drawText和drawBitmap画下文字和图片,直接贴上完整的源码:(在源码注释里面已经把没有讲到的方法大致提了一下实现的原理)
自定义ItemDecoration的代码:
Activity中调用的代码
只要理解了最开始提到的ItemDecoration 的3个主要方法,再处理一下文字分组的逻辑实现起来不会太难,花点耐心还是能写出来的.