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