博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RecyclerView使用指南(五)—— 实现吸顶效果
阅读量:7010 次
发布时间:2019-06-28

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

声明:原创作品,转载请注明出处:

前一篇文字我讲解了ItemDecoration的使用方式,这篇文章默认大家已经读过,所以,不熟悉ItemDecoration的同学请先去看前一篇文章。 OK,我们先来看一下我们将要实现的效果:

一、实现带有Section的样式

我们先重写getItemOffsets()方法,增加outRect的高度,然后重写onDraw()方法,画出一个rectangle。代码如下:

public class DemoItemDecoration extends RecyclerView.ItemDecoration {    private int mSectionHeight = 80;    private Paint mPaint;    public DemoItemDecoration() {        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaint.setColor(Color.BLUE);    }    @Override    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {        super.getItemOffsets(outRect, view, parent, state);        outRect.top = mSectionHeight;    }    @Override    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {        super.onDraw(c, parent, state);        int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            View child = parent.getChildAt(i);            float sectionLeft = parent.getLeft();            float sectionTop = child.getTop() - mSectionHeight;            float sectionRight = parent.getWidth();            float sectionBottom = child.getTop();            c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaint);        }    }    @Override    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {        super.onDrawOver(c, parent, state);    }}复制代码

实现了这样一个效果:

二、实现分组

刚刚实现的每一个Item都有section,这与实际需求时不符的,那么我们要把数据进行分组,每一组的第一条Item上面才有section,这里,为了让ItemDecoration不与数据源发生直接关系,我们新增一个GroupBean类来描述是否需要增加section。如下:

public class GroupBean {    private int mGroupId;    private int mGroupPosition;    private boolean mIsFirst;    private boolean mIsLast;    public GroupBean(int groupId, int groupPosition, boolean isFirst, boolean isLast) {        mGroupId = groupId;        mGroupPosition = groupPosition;        this.mIsFirst = isFirst;        this.mIsLast = isLast;    }    public int getGroupId() {        return mGroupId;    }    public int getGroupPosition() {        return mGroupPosition;    }    public boolean isFirst() {        return mIsFirst;    }    public boolean isLast() {        return mIsLast;    }}复制代码

然后改写我们的getItemOffsets()方法和onDraw()方法,只有每一个分组的第一条Item才显示section。代码如下:

public class DemoItemDecoration extends RecyclerView.ItemDecoration {    private int mSectionHeight = 80;    private Paint mPaintSection;    private Paint mPaintText;    private List
mGroupBeans; public DemoItemDecoration(List
groupBeans) { //数据 mGroupBeans = groupBeans; //画笔 mPaintSection = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintSection.setColor(Color.BLUE); mPaintText = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintText.setTextSize(60); mPaintText.setColor(Color.YELLOW); } @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int position = parent.getChildAdapterPosition(view); if (mGroupBeans.get(position).isFirst()) { outRect.top = mSectionHeight; } } @Override public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDraw(c, parent, state); } @Override public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDrawOver(c, parent, state); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); int position = parent.getChildAdapterPosition(child); GroupBean groupBean = mGroupBeans.get(position); if (mGroupBeans.get(position).isFirst()) { float sectionLeft = parent.getLeft(); float sectionTop = child.getTop() - mSectionHeight; float sectionRight = parent.getWidth(); float sectionBottom = child.getTop(); c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaintSection); c.drawText(String.valueOf(groupBean.getGroupId()), sectionLeft, sectionBottom - 5, mPaintText); } } }}复制代码

最后,在Activity中进行ItemDecoration与GroupBean列表的绑定,以及ItemDecoration与RecyclerView的绑定:

private void initRv() {        List
groupBeans = new ArrayList<>(); //根据RecyclerView的数据源,设置需要增加section的item for (Data data : mList) { //这里就是模拟一下,所以我取4的倍数增加section int i = mList.indexOf(data); int groupId = i / 4; int groupPosition = i % 4; GroupBean groupBean = null; //这里是假数据嘛,4的倍数有section,那余数是3的时候肯定是分组的最后一个啦 if (groupPosition == 0) { groupBean = new GroupBean(groupId, groupPosition, true, false); } if (groupPosition == 3) { groupBean = new GroupBean(groupId, groupPosition, false, true); } groupBeans.add(groupBean); } RecyclerView recyclerView = findViewById(R.id.rv); recyclerView.setAdapter(new SingleItemAdapter(mList)); recyclerView.addItemDecoration(new DemoItemDecoration(groupBeans)); }复制代码

我们来看下效果:

好,这样,我们就实现了分组的效果,但是我们想要的吸顶效果,section是应该显示到Item图层的上方的,那么我们使用onDraw()方法来实现,显然是不合理的,既然如此,我们就将onDraw()方法中的内容剪切到onDrawOver()中好了~

三、实现section在列表顶部悬浮

实现吸顶效果,我们还需要做到让我们的section在列表顶部悬浮,来分析一下逻辑:

  • 每个分组的第一条数据需要有section
  • 列表的最上方必须显示一个section 现在来修改一下onDrawOver(),实现一下:
@Override    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {        super.onDrawOver(c, parent, state);        int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            View child = parent.getChildAt(i);            int position = parent.getChildAdapterPosition(child);            GroupBean groupBean = mGroupBeans.get(position);            //所有分组的第一条数据有section            if (groupBean.isFirst()) {                float sectionLeft = parent.getLeft();                float sectionTop = child.getTop() - mSectionHeight;                float sectionRight = parent.getWidth();                float sectionBottom = child.getTop();                c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaintSection);                c.drawText(String.valueOf(groupBean.getGroupId()), sectionLeft, sectionBottom - 5, mPaintText);            }            //列表的最上方显示section信息(这里section是第一条显示的条目所对应的groupId)            LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();            int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();            if (position == firstVisibleItemPosition) {                float sectionLeft = parent.getLeft();                float sectionTop = parent.getTop();                float sectionRight = parent.getWidth();                float sectionBottom = parent.getTop() + mSectionHeight;                c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaintSection);                c.drawText(String.valueOf(groupBean.getGroupId()), sectionLeft, sectionBottom - 5, mPaintText);            }        }    }复制代码

来看下效果:

嗯,我们实现了一个吸顶效果,但是两个section进行更替的特效显得比较粗糙啊,我们想要的是下面的section将上面的section顶上去,OK,我们再进行优化一下。

四、优化section更替的特效

我们仔细观察上面的效果图,当下面的section向上移动的时候,上面的section没有移动,所以,看起来下面的section直接覆盖到了它的上面。 那么上面的section应该在什么时机进行移动呢?它的底边应该是在该分组中最后一个Item的底部的上方,所以,我们更改,当section的底部低于“分组中最后一个Item”时,section整体上移,移动的距离就是section的高度与条目底部的差。 我们只需更改onDrawOver()方法的代码:

@Override    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {        super.onDrawOver(c, parent, state);        int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            View child = parent.getChildAt(i);            int position = parent.getChildAdapterPosition(child);            GroupBean groupBean = mGroupBeans.get(position);            //所有分组的第一条数据有section            if (groupBean.isFirst()) {                float sectionLeft = parent.getLeft();                float sectionTop = child.getTop() - mSectionHeight;                float sectionRight = parent.getWidth();                float sectionBottom = child.getTop();                c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaintSection);                c.drawText(String.valueOf(groupBean.getGroupId()), sectionLeft, sectionBottom - 5, mPaintText);            }            //列表的最上方显示section信息(这里section是第一条显示的条目所对应的groupId)            LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();            int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();            if (position == firstVisibleItemPosition) {                //如果是本组的最后一条,section的底部就不能低于这个条目的底部                if (groupBean.isLast()) {                    //当条目的底部已经高于section的时候,section应该随着条目的底部往上移动                    if (child.getBottom() < mSectionHeight) {                        float sectionLeft = parent.getLeft();                        float sectionTop = parent.getTop() - (mSectionHeight - child.getBottom());                        float sectionRight = parent.getWidth();                        float sectionBottom = parent.getTop() + mSectionHeight - (mSectionHeight - child.getBottom());                        c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaintSection);                        c.drawText(String.valueOf(groupBean.getGroupId()), sectionLeft, sectionBottom - 5, mPaintText);                    } else {                        float sectionLeft = parent.getLeft();                        float sectionTop = parent.getTop();                        float sectionRight = parent.getWidth();                        float sectionBottom = parent.getTop() + mSectionHeight;                        c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaintSection);                        c.drawText(String.valueOf(groupBean.getGroupId()), sectionLeft, sectionBottom - 5, mPaintText);                    }                } else {                    float sectionLeft = parent.getLeft();                    float sectionTop = parent.getTop();                    float sectionRight = parent.getWidth();                    float sectionBottom = parent.getTop() + mSectionHeight;                    c.drawRect(sectionLeft, sectionTop, sectionRight, sectionBottom, mPaintSection);                    c.drawText(String.valueOf(groupBean.getGroupId()), sectionLeft, sectionBottom - 5, mPaintText);                }            }        }    }复制代码

在Activity中添加数据:

private void initRv() {        List
groupBeans = new ArrayList<>(); //根据RecyclerView的数据源,设置需要增加section的item for (Data data : mList) { //这里就是模拟一下,所以我取4的倍数增加section int i = mList.indexOf(data); int groupId = i / 4; int groupPosition = i % 4; GroupBean groupBean; //这里是假数据嘛,4的倍数有section,那余数是3的时候肯定是分组的最后一个啦 if (groupPosition == 0) { groupBean = new GroupBean(groupId, groupPosition, true, false); } else if (groupPosition == 3) { groupBean = new GroupBean(groupId, groupPosition, false, true); } else { groupBean = new GroupBean(groupId, groupPosition, false, false); } groupBeans.add(groupBean); } RecyclerView recyclerView = findViewById(R.id.rv); recyclerView.setAdapter(new SingleItemAdapter(mList)); recyclerView.addItemDecoration(new DemoItemDecoration(groupBeans)); }复制代码

最后看一下效果:

总结

这篇文章我们实现了一个吸顶效果的特效,是属于比较高级的用法了,关于ItemDecoration的用法也用它进行收尾了。另外,示例代码中的冗余代码比较多,主要是为了看起来容易理解,请小朋友们在使用过程中合理地优化代码。

参考文献

系列文章

转载于:https://juejin.im/post/5bfc9ce2e51d453fb331b929

你可能感兴趣的文章
今年的9月,只能怀念
查看>>
Linux 日志
查看>>
我的友情链接
查看>>
【Android】AndroidStudio空指针解决之:listview与adapter的使用报空 java.lang.NullPointerException...
查看>>
Rsync实现Windows系统与Linux文件同步
查看>>
简单工厂模式( Simple Factory Pattern )
查看>>
CentOS 7安装laravel 5.5.28
查看>>
SQL Server 2005 创建Oracle10g 的链接服务器
查看>>
菜单项启动Activity
查看>>
Ubuntu下安装Python的Tkinter和Pmw库
查看>>
安装Nginx+Lua开发环境
查看>>
nginx nginx.pid无故文件丢失,日志无法正常轮转
查看>>
我的友情链接
查看>>
XML中元素VS属性
查看>>
wepy - 小程序快速开发框架
查看>>
nodejs找不到express命令
查看>>
ubuntu13.04通过lxc搭建容器java运行环境
查看>>
RHCE官方培训笔记---分享
查看>>
top命令是Linux下常用的性能分析
查看>>
使用memcached缓存tomcat7会话信息
查看>>