Android ItemDecoration打造分组悬浮
参考博客:http://www.jianshu.com/p/359b7524fa88
首先看一下实现效果
QQ20170630-122625.gif关于 ItemDecoration 是什么能做什么不在这篇文章的包含范围以内,各位可以自行百度和google,这边主要是讲述使用 ItemDecoration 实现分组悬浮的效果。
前面简单的实现数据展示这里就不做叙述了,都是简单的操作,我们直接切入 ItemDecoration 自定义与使用。
一、首先自定义一个 ItemDecoration
新建一个类,继承 RecyclerView.ItemDecoration 类,并实现四个方法,分别是构造方法、onDraw()、onDrawOver()、getItemOsets()。
- 构造方法:用于后面的参数传递,需要将 RecyclerView 显示的数据传入其中。
- onDraw(): 可以进行绘制,但是是绘制在 ItemView 的下方。
- onDrawOver(): 在 itemview 的上面进行绘制,不会受到 itemview 的影响。
- getItemOffsets(): 设置 itemview 四周的间隙,最后设置的 left、top、right、bottom 都会在 RecyclerView 进行 itemview 布局测量的时候计入 itemview 的 margin 当中。
对于具体介绍可以参考这篇文章 深入理解 RecyclerView 系列之一:ItemDecoration
还有就是我们要明白这三个方法在 ItemDecoartion 中是怎么调用的,建议先使用 Log 输出查看一下,是先调用 getItemOffsets() 有多少个 item 就会调用多少次,完成以后再调用 onDraw() ,然最后 onDrawOver(),后面两个方法金调用一次,而当 RecyclerView 每滑动一次上述步骤就会重新走一波。
二、对数据进行分组
我们不能一口吃掉一个胖子,一步一步来分组悬浮第一步对信息进行分组。我们的目标是在分组之间添加分割线完成分组。
所以这一步操作我们要在 getOffsets() 中执行,主要代码如下,注释比较详细科技直接看代码,我的模拟数据为一组List<String>,循环加入30个数据,每组5条数据共六组,大家看上面演示就知道。
/* 分组头部高度
![
![](http:https://img.haomeiwen.com/i2904262/a1abb1ce9b90b260.gif?imageMogr2/auto-orient/strip)
](http:https://img.haomeiwen.com/i2904262/e7adbc8f81ef4642.gif?imageMogr2/auto-orient/strip)
*/
private int offsesSize = 30;
private List<String> strings;
/* 构造方法传入数据 */
public DemoItemDecoration(List<String> strings) {
super();
this.strings = strings;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
/* 当前 item 的位置 */
int position = parent.getChildAdapterPosition(view);
/* 判断当前 item 是否和上一个 item 属于同一组,不同就需要设置分割线 */
if (beforAndLater(position)) {
outRect.set(0, offsesSize, 0, 0);
}
}
/**
* e
* 当前 item 的数据和上一个数据进行比对,分组信息是否有差异。
* @param postition
* @return
*/
private boolean beforAndLater(int postition) {
/* positon 要大于零,要小于数据数量 */
if (postition > 0) {
/* 获取当前 item 的数据 */
String beforStr = strings.get(postition);
/* 获取上一个 item 的数据 */
String laterStr = strings.get(postition - 1);
/* 进行比对 */
if (beforStr.substring(0).equals(laterStr.substring(0))) {
return false;
} else {
return true;
}
}
/* 第一条数据必定设置分组头部间隙 */
if (postition == 0) {
return true;
}
return false;
}
效果如下:
QQ20170630-161426.gif三、为头部绘制文字
分组成功了,但是每组头部并没有显示组名,现在我们来为分组头部添加组名。这个操作我们要在 onDraw() 方法进行操作。首先我们需要在构造方法中初始化两个 Paint(画笔) 一个背景,一个文字,这样我们在 onDraw() 中使用 canvas 进行绘制。
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
/* 获取子view个数 */
int itemSum = parent.getChildCount();
/* 获取 item 左右的 margin */
int itemLeft = parent.getPaddingLeft();
int itemRifht = parent.getWidth() - parent.getPaddingRight();
/* 遍历 item 给需要绘制分组信息的绘制头部*/
int j = 0;
for (int i = 0; i < itemSum; i++) {
/* 获取子 View */
View childView = parent.getChildAt(i);
/* 获取子 view的位置 */
int position = parent.getChildAdapterPosition(childView);
/* 获取子 view 的布局参数,方便确定头部位置 */
RecyclerView.LayoutParams childParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int itemTop = childView.getTop() - childParams.topMargin - offsesSize;
int itemButtom = childView.getTop() - childParams.topMargin;
/* 判定当前子 view 是否需要绘制头部,注意位置 */
if (beforAndLater(position)) {
/* 绘制头部背景 */
c.drawRect(itemLeft, itemTop, itemRifht, itemButtom, testPaint);
/* 绘制文字 */
c.drawText(strings.get(position), itemLeft, itemButtom, textPaint);
}
}
}
效果如下:
QQ20170630-163019.gif四、绘制悬浮头部
高地已破,现在只要拔掉两个狼牙,干掉水晶我们就 Victory 了!
记住!记住!ItemDecoration 里面三个方法的调用步骤!这个很重要。我就在这个地方猜坑了,耽误的很多时间。
好了,我的思路是这样的。获取第一个可见的 itemview ,然后判断后面的一个 itemview 是否和第一个可见的 itemview 属于同一分组,如果不同我们就获取后面一个 itemview 底部距离 RecyclerView 顶部的距离(即 view.getBottom),然后与悬浮头部的固定 bottom 距离做比较,去相对较小的一个,然后进行绘制,代码如下:
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
/* 获取第一个可见view itemview */
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) parent.getLayoutManager();
int firstChildViewPosition = linearLayoutManager.findFirstVisibleItemPosition();
/* 这里出现了一个bug,用parent.getCildAt(),出现childView为空的现象,所以用这个方法 */
View firseChildView = parent.findViewHolderForLayoutPosition(firstChildViewPosition).itemView;
int left = parent.getLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int top = parent.getPaddingTop();
int bottom = parent.getPaddingTop() + offsesSize;
/**
* 上拉时,判定位于第一个可见 item 与下一个 item 是不是属于同一分组的,如果是悬浮条的位置( bottom )依然取 parent.getPaddingTop() + offsesSize
* 当不是时,在获取可一个可见 itemview 的 Bottom(getBottom() 它的数值就是 itemview 距离 parent 顶部的距离)与 bottom 大小进行比较,哪个小就选哪个进行绘制悬浮条。
* 下拉亦是如此。
*
*/
if (nowContrastNext(firstChildViewPosition)) {
bottom = Math.min(firseChildView.getBottom(), bottom);
}
c.drawRect(0, top, right, bottom, testPaint);
c.drawText(strings.get(firstChildViewPosition).substring(0), left, bottom, textPaint);
}
然后运行就能得到头部悬停的效果了!