博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RecyclerView学习(四)----ItemDecoration实现的城市导航列表(下)
阅读量:6923 次
发布时间:2019-06-27

本文共 4985 字,大约阅读时间需要 16 分钟。

之前用RecyclerView实现了写过一篇城市导航列表:

关于自定义的导航条,滑动监听,汉字转拼音等零碎知识,大家可以查看我之前那篇博客。

今天主要说的是悬停列表的实现,之前的实现方式是每一个RecyclerView的item的布局里面都包含一个头部布局,然后判断当前item和上一个item的头部布局里的索引字母是否相同,来决定是否展示item的头部布局。这种实现方式显得布局冗余,效率降低,而且不够优雅。今天这里我用ItemDecoration来实现城市列表的头部悬停。

ItemDecoration是用来修饰RecyclerView里的Item,ItemDecoration类主要有三个方法:

public void onDraw(Canvas c, RecyclerView parent, State state)public void onDrawOver(Canvas c, RecyclerView parent, State state)public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)

这三个方法作用依次是:

onDraw():可以实现类似绘制背景的效果,item的内容在上面

onDrawOver():可以绘制在内容的上面,覆盖item的内容

getItemOffsets():可以实现类似padding的效果

onDraw()的绘制会先于子ItemView的绘制,如果你在onDraw()方法中绘制的东西在子ItemView边界内,就会被ItemView盖住,所以我们一般先调用getItemOffsets()创造空间。而onDrawOver()会在子ItemView绘制之后再绘制,绘制的内容会覆盖在子ItemView上。

执行顺序是先执行ItemDecoration的onDraw()、再执行子ItemView的onDraw()、再执行ItemDecoration的onDrawOver()。

下面我们通过改造这个例子来分析这三个方法:

1.getItemOffsets()

我移除了之前项目里用到的悬停,侧边导航栏与滑动监听还保留,看下现在的效果:

这里写图片描述

ok,现在我们来自定义一个ItemDecoration:

public class StickyDecoration extends RecyclerView.ItemDecoration {
private int topHeight; public StickyDecoration(Context context) { Resources res = context.getResources(); topHeight = res.getDimensionPixelSize(R.dimen.top); } @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 (isFirstInGroup(position)) { outRect.top = topHeight; } else { outRect.top = 0; } } private boolean isFirstInGroup(int position) { boolean isFirst; if (position == 0) { isFirst = true; } else { if (CityActivity.cityList.get(position).getFirstPinYin(). equals(CityActivity.cityList.get(position - 1).getFirstPinYin())) { isFirst = false; } else { isFirst = true; } } return isFirst; }}

继承自RecyclerView.ItemDecoration,然后重写构造方法,并且重写getItemOffsets()方法。进行判断,该item如果是第一次出现该字母,就给这个item的上方绘制一个40dp的高度,否则就不处理。Activity中使用:

recyclerView.addItemDecoration(new StickyDecoration(getApplicationContext()));

最后我们看一下效果:

这里写图片描述

可以看到,在每一个字母首次出现的item上,都多了一个40dp的空间,这里用来放我们的索引字母。

2.onDraw()

onDraw()可以实现类似绘制背景的效果,item的内容在上面,我们通过getItemOffsets()创造了空间,然后用onDraw()方法绘制字母索引:

private TextPaint textPaint;    private Paint paint;    private int topHeight;    public StickyDecoration(Context context) {        Resources res = context.getResources();        paint = new Paint();        paint.setColor(res.getColor(R.color.colorAccent));        textPaint = new TextPaint();        textPaint.setAntiAlias(true);        textPaint.setTextSize(60);        textPaint.setColor(Color.WHITE);        topHeight = res.getDimensionPixelSize(R.dimen.top);    }    @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 (isFirstInGroup(position)) {            outRect.top = topHeight;        } else {            outRect.top = 0;        }    }    @Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        super.onDraw(c, parent, state);        int left = parent.getPaddingLeft();        int right = parent.getWidth() - parent.getPaddingRight();        int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            View view = parent.getChildAt(i);            int position = parent.getChildAdapterPosition(view);            String textLine = CityActivity.cityList.get(position).getFirstPinYin();            if (position == 0 || isFirstInGroup(position)) {                float top = view.getTop() - topHeight;                float bottom = view.getTop();                c.drawRect(left, top, right, bottom, paint);//绘制红色矩形                c.drawText(textLine, left + 30, bottom - 30, textPaint);//绘制文本            }        }    }

这里的onDraw()方法与自定义View的onDraw()一样,只不过这里绘制的对象是RecyclerView子item的view。我们看下最后实现的效果:

这里写图片描述

OK,在首次出现该字母的item上都出现了字母索引,接下来就是悬停的实现了。

3.onDrawOver()

onDrawOver()方法会绘制在内容的上面,覆盖item的内容,所以我们拿来做悬停很合适。

@Override    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {        super.onDrawOver(c, parent, state);        int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();        int left = parent.getPaddingLeft();        int right = parent.getWidth() - parent.getPaddingRight();        c.drawRect(left, 0, right, topHeight, paint);//绘制红色矩形        String text = CityActivity.cityList.get(position).getFirstPinYin();        c.drawText(text, 30, topHeight - 30, textPaint);//绘制文本    }

实现的代码其实很简单,拿到RecyclerView可见区域第一个item的position,得到大写字母。依次绘制红色背景与文字即可。最后实现的效果如下:

这里写图片描述

OK,和之前实现的效果还是没什么区别的。

项目完整源码已经上传到我的github上,源码地址:

欢迎Star,fork,提issues,一起进步!

你可能感兴趣的文章
热点推荐:秒杀系统架构分析与实战
查看>>
RAID5+LVM
查看>>
压缩与打包
查看>>
查询公司服务器类型
查看>>
我的友情链接
查看>>
(一)hadoop系列之__XP环境下搭建linux虚拟机
查看>>
我的友情链接
查看>>
LAMP+LVS+KEEPALIVED(五)
查看>>
uboot的作用和启动方式
查看>>
1.2关系数据库
查看>>
SpringCloud
查看>>
RHEL主机安全检查机制: TCP Wrappers、Xinetd
查看>>
泛型编程之类模板
查看>>
salt安装
查看>>
linux运维基础1
查看>>
Hyper-V Server虚拟机移动性
查看>>
Visual Studio 2014 预览版 CTP3 发布了!可以下载
查看>>
protoc 在linux下的安装
查看>>
jq上百个input 做提交不能为空的验证
查看>>
网络篇
查看>>