RecyclerView achieves ceiling effect

Table of contents

1. Item Decoration

2. Realize the ceiling effect of RecyclerView

1. Implement a simple RecyclerView.

2. Draw the dividing line through ItemDecoration

3. Draw the group name for each group

4. Realize the ceiling effect

complete demo


1. Item Decoration

        \bullet ItemDecoration allows the application to add specific drawings or layout offsets to specific Views, which is very useful for drawing dividing lines between Views, visual grouping boundaries, and so on.

        \bullet When we call the addItemDecoration() method to add decoration, RecyclerView will call the onDraw method of this class to draw the dividing line, that is to say: the dividing line is drawn.

RecyclerView.addItemDecoration()

        \bullet RecyclerView.ItemDecoration, this class is an abstract class, the official currently only provides an implementation class DividerItemDecoration.

public abstract static class ItemDecoration
public class DividerItemDecoration extends RecyclerView.ItemDecoration

 There are 3 methods in it:

          \bullet onDraw(): Draws any appropriate decorations on the canvas provided to the RecyclerView. Anything drawn via this method will be drawn before the item view is drawn, so will appear below the view.

public void onDraw(Canvas c, RecyclerView parent, State state) {
    
    
            onDraw(c, parent);
        }

 drawing effect

          \bullet onDrawOver(): Draws any appropriate decorations on the canvas provided to the RecyclerView. Anything drawn via this method will be drawn after the item view is drawn, and thus will appear above the view.

public void onDrawOver(Canvas c, RecyclerView parent, State state) {
    
    
            onDrawOver(c, parent);
        }

 drawing effect

         \bullet getItemOffsets(): Retrieves any offsets for a given item. Each field of outRect specifies how many pixels the item view should be inset, similar to padding or margin. The default implementation sets outRect's bounds to 0 and returns.

public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
    
    
            getItemOffsets(outRect, ((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),
                    parent);
        }

2. Realize the ceiling effect of RecyclerView

1. Implement a simple RecyclerView.

        The implementation details of the following RecyclerView are omitted.

 

2. Draw the dividing line through ItemDecoration

        Customize ItemDecoration.

        Create a fruitDecotion class that inherits ItemDecoration and implements three methods: onDraw, onDrawover, and getItemOffsets.

        RecyclerView calls custom fruitDecoration

RecyclerView.addItemDecoration(new fruitDecoration(this))

        Draw a dividing line in getItemOffsets. When it is a group name, reserve a larger space (that is, the height of the dividing line is larger than that of the ordinary dividing line).

@Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.getItemOffsets(outRect, view, parent, state);
 
        if (parent.getAdapter() instanceof fruitAdapter){
    
    
            // parent.getAdapter()获取到当前RecyclerView的Adapter
            fruitAdapter adapter = (fruitAdapter) parent.getAdapter();
            // 获取当前view的位置
            int position = parent.getChildLayoutPosition(view);
            if (adapter.isGroupHeader(position)) {
    
    
                // 如果是头部,则预留更大的地方
                outRect.set(0, groupHeaderHeight, 0, 0);
            }else {
    
    
                outRect.set(0, 4, 0, 0);
            }
 
        }

 achieve effect

3. Draw the group name for each group

        When it is a group name, draw the group name where the larger dividing line is. Implemented in the onDraw() method

@Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.onDraw(c, parent, state);
 
        if (parent.getAdapter() instanceof fruitAdapter) {
    
    
            fruitAdapter adapter = (fruitAdapter) parent.getAdapter();
            //当前屏幕中item的个数
            int count = parent.getChildCount();
            // 获取当前RecyclerView距离屏幕左边的padding
            int left = parent.getPaddingLeft();
            // right == RecyclerView的宽度 - RecyclerView的右padding
            int right = parent.getWidth() - parent.getPaddingRight();
            for (int i = 0; i < count; i++) {
    
    
                //获取对应 i 的 view
                View view = parent.getChildAt(i);
                // 获取 i 的 view 的布局位置
                int position = parent.getChildLayoutPosition(view);
                // 判断是否是头部
                boolean isGroupHeader = adapter.isGroupHeader(position);
                // i 的 view 是头部并且 当前view的top位置距离屏幕的顶部还有距离
                if(isGroupHeader && view.getTop() - groupHeaderHeight - parent.getPaddingTop() >=0) {
    
    
                    c.drawRect(left, view.getTop() - groupHeaderHeight, right, view.getTop(), headPaint);
                    String groupName = adapter.getGroupName(position);
                    textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                    c.drawText(groupName, left + 20, view.getTop() - groupHeaderHeight / 2f + textRect.height() / 2f, textPaint);
                }else if(view.getTop() - groupHeaderHeight - parent.getPaddingTop() >=0){
    
    
                    //分割线
                    c.drawRect(left, view.getTop() - 4, right, view.getTop(), headPaint);
                }
            }
       }
    }

achieve effect 

4. Realize the ceiling effect

        Because the onDrawOver method is drawn after the itemView is drawn, the ceiling of the group name is written in the onDrawOver method. It should be noted that when our second group name reaches the top, we need to replace the current top group name.

 @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.onDrawOver(c, parent, state);
        if (parent.getAdapter() instanceof fruitAdapter) {
    
    
            fruitAdapter adapter = (fruitAdapter) parent.getAdapter();
            //TODO 返回可见区域内的第一个item的position
            int position =((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
            // 获取第一个item的view
            View itemView = parent.findViewHolderForAdapterPosition(position).itemView;
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();
            int top = parent.getPaddingTop();
            //TODO 当第二个是头部时,
            boolean isGroupHeader = adapter.isGroupHeader(position + 1);
            if (isGroupHeader) {
    
    
                int bottom = Math.min(groupHeaderHeight, itemView.getBottom() - parent.getPaddingTop());
 
                c.drawRect(left, top, right, top + bottom, headPaint);
                String groupName = adapter.getGroupName(position);
                textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                c.drawText(groupName, left + 20, top + bottom - groupHeaderHeight / 2f
                        + textRect.height() / 2f, textPaint);
 
            }else {
    
     // 如果不是头部, 即普通的itemView,则当前的头部一直固定在顶部
                c.drawRect(left, top, right, top + groupHeaderHeight, headPaint);;
                String groupName = adapter.getGroupName(position);
                textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                c.drawText(groupName, left + 20, top + groupHeaderHeight / 2f
                        + textRect.height() / 2f, textPaint);
            }
        }
    }

achieve effect

complete demo

Link: https://pan.baidu.com/s/1NmFA61jvhl0oaN5IgBm4CQ 
Extraction code: 41f0

Reprinted: https://blog.csdn.net/qq_44950283/article/details/128362193

Guess you like

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