RecyclerView floating title how to achieve?

    / / add itemDecoration
    public void addItemDecoration(@NonNull RecyclerView.ItemDecoration decor) {
        this.addItemDecoration(decor, -1);
    }
Copy the code

Take a look at the source code of ItemDecoration

public abstract static class ItemDecoration {
        public ItemDecoration(a) {}// Draw method
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.onDraw(c, parent);
        }

        / * *@deprecated* /
        @Deprecated
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {}//onDraw completes the execution method
        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.onDrawOver(c, parent);
        }

        / * *@deprecated* /
        @Deprecated
        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {}/ * *@deprecated* /
        @Deprecated
        public void getItemOffsets(@NonNull Rect outRect, int itemPosition, @NonNull RecyclerView parent) {
            outRect.set(0.0.0.0);
        }
        // Get Item location information
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.getItemOffsets(outRect, ((RecyclerView.LayoutParams)view.getLayoutParams()).getViewLayoutPosition(), parent); }}Copy the code

New MyItemDecoration inherits ItemDecoration and overloads these three methods

  • Gets the display position of headerViewHolder
  • Draw the headerViewHolder to display
   public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.onDraw(c, parent);
        }
   public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.onDrawOver(c, parent);
        }
 public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.getItemOffsets(outRect, ((RecyclerView.LayoutParams)view.getLayoutParams()).getViewLayoutPosition(), parent);
        }        
Copy the code
private final StickyHeaderAdapter mAdapter; / / interface
public interface StickyHeaderAdapter {
    int getHeaderId(int position);/ / get the position
    / / create HeaderViewHolder
    RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position);
    // Bind implementation
    void onBindHeaderViewHolder(RecyclerView.ViewHolder holder, int position);
}
private final SparseArray<Rect> mHeaderRectSet = new SparseArray<>(); // Store Rect objects
private final HashMap<Integer, RecyclerView.ViewHolder> mHeaderViews = new HashMap<>();// Store ViewHolder corresponding to position
Copy the code
@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);
        int headerHeight = 0;
        // It is best to add this judgment when using adapterPosition
        if(position ! = RecyclerView.NO_POSITION) {// Get the height of ItemDecoration
            View header = getHeader(parent, position);
            headerHeight = header.getHeight();
        }
        outRect.set(0, headerHeight, 0.0); // Change the hearderHeight height
    }
    
    /** * get the custom Header *@param parent
     * @param position
     * @return* /
    public View getHeader(RecyclerView parent, int position) {
        // Get the header id of each group by position
        final int headerId = mAdapter.getHeaderId(position);
        // Get the reorganized header view from the saved header view array with the header ID
        RecyclerView.ViewHolder holder = mHeaderViews.get(headerId);
        // If it is empty, create it through adapert
        if (holder == null) {
            / / create HeaderViewHolder
            holder = mAdapter.onCreateHeaderViewHolder(parent, position);
            View header = holder.itemView;
            header.setClickable(true);
            header.setFocusable(true);
            // Bind data
            mAdapter.onBindHeaderViewHolder(holder, position);
            // Measure the View and layout
            int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
            int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
            // The parent View's MeasureSpec and the child View's LayoutParams and padding are the parent View's MeasureSpec
            int childWidth = ViewGroup.getChildMeasureSpec(widthSpec,
                    parent.getPaddingLeft() + parent.getPaddingRight(), header.getLayoutParams().width);
            int childHeight = ViewGroup.getChildMeasureSpec(heightSpec,
                    parent.getPaddingTop() + parent.getPaddingBottom(), header.getLayoutParams().height);
            // Make a measurement
            header.measure(childWidth, childHeight);
            // According to the measured width and height position
            header.layout(0.0, header.getMeasuredWidth(), header.getMeasuredHeight());
            // Save the header view in the array to avoid repeated creation
            mHeaderViews.put(headerId, holder);
        }
        return holder.itemView;

    }
    
   @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        changDrawOver(c, parent);
    }

    private void changDrawOver(@NotNull Canvas c, @NotNull RecyclerView parent) {
        //mHeaderRectSet is the click area where the headers displayed on the screen are stored, and the data is cleared each time the header is redrawn
        mHeaderRectSet.clear();
        final int count = parent.getChildCount();
        // Iterate over the loaded item on the screen
        for (int layoutPos = 0; layoutPos < count; layoutPos++) {
            final View child = parent.getChildAt(layoutPos);
            // Get the position of the item in the list data
            final int adapterPos = parent.getChildAdapterPosition(child);
            // Only the uppermost item or item with a header is drawn
            if(adapterPos ! = RecyclerView.NO_POSITION && (layoutPos ==0 || hasHeader(adapterPos))) {
                View header = getHeader(parent, adapterPos);
                c.save();
                // Get the starting position of the drawing header (left,top)
                final int left = child.getLeft();
                final int top = getHeaderTop(parent, child, header, adapterPos, layoutPos);
                // Move the canvas to the drawing position
                c.translate(left, top);
                / / draw the header
                header.draw(c);
                c.restore();
                // Save the area of the drawn header
                mHeaderRectSet.put(adapterPos, newRect(left, top, left + header.getWidth(), top + header.getHeight())); }}}Copy the code

RecyclerView ItemDecoration how to create HeaderViewHolder click events?

    // Add Item Touch listener
    public void addOnItemTouchListener(@NonNull RecyclerView.OnItemTouchListener listener) {
        this.mOnItemTouchListeners.add(listener);
    }
    
      @Override
    public boolean onInterceptTouchEvent(@NonNull RecyclerView view, @NonNull MotionEvent e) {
        // Pass the event to the GestureDetector class for processing. Use the value returned by onSingleTapUp to determine whether to intercept the event
        boolean tapDetectorResponse = this.mTapDetector.onTouchEvent(e);
        // If the click is in the header area, the event is intercepted
        if (tapDetectorResponse) {
            return true;
        }

        if (e.getAction() == MotionEvent.ACTION_UP) {
            int position = mDecor.findHeaderPositionUnder((int) e.getX(), (int) e.getY());
            returnposition ! = -1;
        }
        return false;
    }
    
    mTapDetector = new GestureDetectorCompat(recyclerView.getContext(), new GestureDetector.SimpleOnGestureListener() {

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                // Check whether the click is in the header area based on the click coordinates
                int position = mDecor.findHeaderPositionUnder((int) e.getX(), (int) e.getY());
                if(position ! = -1) {
                    // If position is not equal to -1, click in the header area, and check whether the header needs to respond to the area
                    View header = mDecor.getHeader(mRecyclerView, position);
                    if (header instanceof ViewGroup) {
                        int childCount = ((ViewGroup) header).getChildCount();
                        for (int i = 0; i < childCount; i++) {
                            View childAt = ((ViewGroup) header).getChildAt(i);
                            if (mDecor.findHeaderClickView(childAt, (int) e.getX(), (int) e.getY())) {
                                // If it is in the area where the header needs to respond, the view in that area simulates clicking
                                childAt.performClick();
                                break; }}}return true;
                }
                return false;
            }

            @Override
            public boolean onDoubleTap(MotionEvent e) {
                return true; }});public int findHeaderPositionUnder(int x, int y) {
        for (int i = 0; i < mHeaderRectSet.size(); i++) {
            Rect rect = mHeaderRectSet.get(mHeaderRectSet.keyAt(i));
            if (rect.contains(x, y)) {
                returnmHeaderRectSet.keyAt(i); }}return -1;
    }
     public boolean findHeaderClickView(View view, int x, int y) {
        if (view == null) return false;
        for (int i = 0; i < mHeaderRectSet.size(); i++) {
            Rect rect = mHeaderRectSet.get(mHeaderRectSet.keyAt(i));
            if (rect.contains(x, y)) {
                Rect vRect = new Rect();
                // The on-screen coordinates of the region that needs to respond to the click event
                vRect.set(rect.left + view.getLeft(), rect.top + view.getTop(), rect.left + view.getLeft() + view.getWidth(), rect.top + view.getTop() + view.getHeight());
                returnvRect.contains(x, y); }}return false;
    }
Copy the code