- 首先得明白一点Recycleview的ItemDecoration非常强大,你可以使用它实现listview的分割线,悬浮窗,甚至一些非常炫的动画。
- 先看下效果图
ItemDecoration
- onDraw():和普通view的onDraw差不多,就是绘制在画布上绘制东西。
- onDrawOver():就是基于onDraw上再次绘制点东西,over的意思。
-
getItemOffsets():Recycleview可以通过他拿到每一个item的间距,所以只需要控制这个间距,并在间距里利用onDrawOver再绘制你想绘制的东西。
实现这个的思路,我们只需要在指定的的行数通过getItemOffsets预留出我们要空出的高度,然后通过onDrawOver绘制出你所希望的view即可。
一:手动构造数据格式,如下,返回list
Goods goods1 = new Goods("人气TOP", "正果拿铁1", "Y27", "默认:大/单糖/热");
Goods goods99 = new Goods("人气TOP", "正果拿铁2", "Y27", "默认:大/单糖/热");
Goods goods91 = new Goods("人气TOP", "正果拿铁3", "Y27", "默认:大/单糖/热");
Goods goods2 = new Goods("大师咖啡", "拿铁", "Y27", "默认:大/单糖/热");
Goods goods3 = new Goods("大师咖啡", "香草拿铁", "Y24", "默认:大/单糖/热");
Goods goods4 = new Goods("大师咖啡", "焦糖拿铁", "Y26", "默认:大/半糖/热");
List<Goods> list = new ArrayList<>();
二:书写自己的ItemDecoration
- getItemOffsets预留空间,只需要在每个数组的第一个数据预留一个高度,比如第一个人气TOP,第一个大师咖啡。
第一个必须预留,当前位置的name和前一个不相等则为预留空间
@Override
public boolean isParent(int position) {
if (position == 0) return true;
if (!list.get(position).getType().equals(list.get(position - 1).getType()))
return true;
return false;
}
//是否为当前最后一个item
protected boolean lastOneInGroup(int position) {
String parentName = mDecorListener.parentName(position);
String nextGroupName;
try {
nextGroupName = mDecorListener.parentName(position + 1);
} catch (Exception e) {
nextGroupName = null;
}
if (nextGroupName == null) {
return false;
}
return !TextUtils.equals(parentName, nextGroupName);//与下一行的name不一样说明当前是最后一行
}
由上isParent判断是第一个的返回你要预留的高度大小,否则为不需要空间0
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view);
if (parent.getLayoutManager() instanceof LinearLayoutManager && mDecorListener.isParent(position)) {
outRect.top = decorationHeight;
return;
}
outRect.top = 0;
}
- 在预留的空间上画上你的view
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
final int itemCount = state.getItemCount(); 全部item的数量
final int childCount = parent.getChildCount(); 可看见的排除悬停的分割线的个数
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
for (int i = 0; i < childCount; i++) {
View childView = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(childView);//就是当前可见每一行的position,从0开始
//默认第一个就是有个Group
if (mDecorListener.isParent(position) || i == 0) {//中第一个位置和可见的第一个才有这个悬停
//绘制悬浮,
int bottom = Math.max(decorationHeight, (childView.getTop() + parent.getPaddingTop()));
//决定当前顶部第一个悬浮Group的bottom,拿到item高度和规定高度对比,只是选择一个合适的高度定义分割线
if (position + 1 < itemCount) {
//下一组的第一个View接近头部
int viewBottom = childView.getBottom();
if (lastOneInGroup(position) && viewBottom < bottom) {
bottom = viewBottom; //如果这个关掉,会覆盖,顶上去效果失去,其实viewBottom逐渐变为0,这样动态的放置即将消失的悬浮拦,看上去就是下一个悬浮拦顶上来的
}
}
drawDecoration(c, position, left, right, bottom, parent);
stickyHeaderPosArray.put(position, bottom);
}
}
}
private void drawDecoration(Canvas c, int position, int left, int right, int bottom, RecyclerView parent) {
c.drawRect(left, bottom - decorationHeight, right, bottom, mGroutPaint);
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
//文字竖直居中显示
float baseLine = bottom - (decorationHeight - (fm.bottom - fm.top)) / 2 - fm.bottom;
//获取文字宽度
mSideMargin = Math.abs(mSideMargin);
c.drawText(mDecorListener.parentName(position), left + mSideMargin, baseLine, mTextPaint);//x轴,baseLine
Rect rect = new Rect();//为了得到当前text的属性
mTextPaint.getTextBounds(mDecorListener.parentName(position), 0, mDecorListener.parentName(position).length(), rect);
//绘制那条横线
c.drawLine(left + mSideMargin * 2 + rect.width(), baseLine - rect.height() / 2, parent.getWidth() -
mSideMargin, baseLine - rect.height() / 2, mTextPaint);
}
完结
转载于:https://blog.51cto.com/8912823/2367243