RecyclerView usage guide - achieve ceiling effect

Let's first take a look at the effect we will achieve:

Please add a picture description

1. Realize the style with Section

We first rewrite the getItemOffsets() method to increase the height of outRect, and then rewrite the onDraw() method to draw a rectangle. code show as below:

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);
    }
}

Such an effect is achieved:

insert image description here

2. Realize grouping

Each Item just implemented has a section, which is inconsistent with the actual demand. Then we need to group the data. The first Item in each group has a section. Here, in order to prevent the ItemDecoration from directly interacting with the data source Relationship, we add a GroupBean class to describe whether to add section. as follows:

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;
    }
}

Then rewrite our getItemOffsets() method and onDraw() method, and only the first Item of each group will display the section. code show as below:

public class DemoItemDecoration extends RecyclerView.ItemDecoration {
    
    
    private int mSectionHeight = 80;
    private Paint mPaintSection;
    private Paint mPaintText;
    private List<GroupBean> mGroupBeans;

    public DemoItemDecoration(List<GroupBean> 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);
            }
        }
    }
}

Finally, bind the ItemDecoration to the GroupBean list in the Activity, and bind the ItemDecoration to the RecyclerView:

    private void initRv() {
    
    
        List<GroupBean> 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));
    }

Let's see the effect:

insert image description here

Well, in this way, we have achieved the effect of grouping, but the ceiling effect we want, the section should be displayed above the Item layer, so it is obviously unreasonable for us to use the onDraw() method to achieve it, since In this way, we will cut the content in the onDraw() method to onDrawOver()~

3. Realize section floating at the top of the list

To achieve the ceiling effect, we also need to let our section float at the top of the list, let's analyze the logic:

  • The first piece of data in each group needs to have a section
  • A section must be displayed at the top of the list.
    Now let’s modify onDrawOver() and implement it:
@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);
            }
        }
    }

Let's see the effect:

Please add a picture description

Well, we have achieved a ceiling effect, but the special effect of the replacement of the two sections is relatively rough. What we want is that the lower section pushes the upper section up. OK, let’s optimize it again.

4. Optimize the special effects of section replacement

We carefully observe the above renderings, when the lower section moves up, the upper section does not move, so it seems that the lower section directly covers it.
So when should the above section be moved? Its bottom edge should be above the bottom of the last Item in the group, so we change that when the bottom of the section is lower than the "last Item in the group", the section moves up as a whole, and the moving distance is the height of the section Difference from the bottom of the entry.
We just need to change the code of the onDrawOver() method:

    @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);
                }
            }
        }
    }

Add data in Activity:

    private void initRv() {
    
    
        List<GroupBean> 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));
    }

Finally look at the effect:

Please add a picture description

Summarize

In this article, we have implemented a special effect of ceiling effect, which is a relatively advanced usage, and we also use it to finish the usage of ItemDecoration. In addition, there are a lot of redundant codes in the sample code, mainly for the sake of easy understanding, please optimize the code reasonably during use.

references

https://blog.csdn.net/briblue/article/details/70211942

series of articles

"RecyclerView User Guide (1) - Basic Use
" RecyclerView User Guide (2) - Multiple ItemLayout"
"RecyclerView User Guide (3) - Add Divider Lines and Click Events
" RecyclerView User Guide (4) - Use ItemDecoration"
"RecyclerView User Guide (5) - Realize Ceiling Effect"

Reprint: https://www.jianshu.com/p/c197e3bf8329

Guess you like

Origin blog.csdn.net/gqg_guan/article/details/132206059