RecycleView懸浮頭部使用筆記
阿新 • • 發佈:2018-12-03
https://github.com/qdxxxx/StickyHeaderDecoration
1.因為頭部不是我想要的,所以直接下載程式碼後進行修改,程式碼:
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import java.util.HashMap; import java.util.Map; public abstract class RecycleViewDecoration extends RecyclerView.ItemDecoration { protected String TAG = "RecycleViewDecoration"; private Paint mHeaderTxtPaint;//文字 private Paint mHeaderContentPaint;//背景 private Paint mBottomLinePaint;//底部線條 private Paint mBitmapPaint;//右邊箭頭 protected int headerHeight = 136;//頭部高度 private int textPaddingLeft = 30;//頭部文字左邊距 private int textPaddingRght=40;//右邊文字邊距 protected int bottomLineHeight=1; private int textSize = 50; private int textColor = Color.BLACK; private int headerContentColor = 0xffeeeeee; private int bottomLineColor=0xffeeeeee; private Bitmap bitmap; private int bitmapPadding=30;//箭頭右邊邊距 private final float txtYAxis; private RecyclerView mRecyclerView; public RecycleViewDecoration() { mHeaderTxtPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mHeaderTxtPaint.setColor(textColor); mHeaderTxtPaint.setTextSize(textSize); mHeaderTxtPaint.setTextAlign(Paint.Align.LEFT); mHeaderContentPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mHeaderContentPaint.setColor(headerContentColor); Paint.FontMetrics fontMetrics = mHeaderTxtPaint.getFontMetrics(); float total = -fontMetrics.ascent + fontMetrics.descent; txtYAxis = total / 2 - fontMetrics.descent; mBitmapPaint=new Paint(Paint.ANTI_ALIAS_FLAG); bitmap= BitmapFactory.decodeResource(BaseApplication.getContext().getResources(), R.mipmap.ic_arrow_right); mBottomLinePaint=new Paint(Paint.ANTI_ALIAS_FLAG); mBottomLinePaint.setColor(bottomLineColor); } private boolean isInitHeight = false; /** * 先呼叫getItemOffsets再呼叫onDraw */ @Override public void getItemOffsets(Rect outRect, View itemView, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, itemView, parent, state); if (mRecyclerView == null) { mRecyclerView = parent; } if (headerDrawEvent != null && !isInitHeight) { View headerView = headerDrawEvent.getHeaderView(0); headerView .measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); headerHeight = headerView.getMeasuredHeight(); isInitHeight = true; } /*我們為每個不同頭部名稱的第一個item設定頭部高度*/ int pos = parent.getChildAdapterPosition(itemView); //獲取當前itemView的位置 String curHeaderName = getHeaderName(pos); //根據pos獲取要懸浮的頭部名 if (curHeaderName == null) { return; } if (pos == 0 || !curHeaderName.equals(getHeaderName(pos - 1))) {//如果當前位置為0,或者與上一個item頭部名不同的,都騰出頭部空間 outRect.top = headerHeight; //設定itemView PaddingTop的距離 } } public abstract String getHeaderName(int pos); public abstract String getHeaderRightText(int pos); private SparseArray<Integer> stickyHeaderPosArray = new SparseArray<>();//記錄每個頭部和懸浮頭部的座標資訊【用於點選事件】 private GestureDetector gestureDetector; @Override public void onDrawOver(Canvas canvas, RecyclerView recyclerView, RecyclerView.State state) { super.onDrawOver(canvas, recyclerView, state); if (mRecyclerView == null) { mRecyclerView = recyclerView; } if (gestureDetector == null) { gestureDetector = new GestureDetector(recyclerView.getContext(), gestureListener); recyclerView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } }); } stickyHeaderPosArray.clear(); int childCount = recyclerView.getChildCount();//獲取螢幕上可見的item數量 int left = recyclerView.getLeft() + recyclerView.getPaddingLeft(); int right = recyclerView.getRight() - recyclerView.getPaddingRight(); String firstHeaderName = null; String headerRirhtText=null; int firstPos = 0; int translateTop = 0;//繪製懸浮頭部的偏移量 /*for迴圈裡面繪製每個分組的頭部*/ for (int i = 0; i < childCount; i++) { View childView = recyclerView.getChildAt(i); int pos = recyclerView.getChildAdapterPosition(childView); //獲取當前view在Adapter裡的pos String curHeaderName = getHeaderName(pos); //根據pos獲取要懸浮的頭部名 String curHeaderRightText=getHeaderRightText(pos); if (i == 0) { firstHeaderName = curHeaderName; headerRirhtText=curHeaderRightText; firstPos = pos; } if (curHeaderName == null) continue;//如果headerName為空,跳過此次迴圈 int viewTop = childView.getTop() + recyclerView.getPaddingTop(); if (pos == 0 || !curHeaderName.equals(getHeaderName(pos - 1))) {//如果當前位置為0,或者與上一個item頭部名不同的,都騰出頭部空間 if (headerDrawEvent != null) { View headerView; if (headViewMap.get(pos) == null) { headerView = headerDrawEvent.getHeaderView(pos); headerView .measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); headerView.setDrawingCacheEnabled(true); headerView.layout(0, 0, right, headerHeight);//佈局layout headViewMap.put(pos, headerView); canvas.drawBitmap(headerView.getDrawingCache(), left, viewTop - headerHeight, null); } else { headerView = headViewMap.get(pos); canvas.drawBitmap(headerView.getDrawingCache(), left, viewTop - headerHeight, null); } } else { canvas.drawRect(left, viewTop - headerHeight, right, viewTop, mHeaderContentPaint); canvas.drawText(curHeaderName, left + textPaddingLeft, viewTop - headerHeight / 2 + txtYAxis, mHeaderTxtPaint); if (!TextUtils.isEmpty(headerRirhtText)){ Rect mBounds = new Rect(); mHeaderTxtPaint.getTextBounds(headerRirhtText,0,headerRirhtText.length(),mBounds); float rightTextWidth=mBounds.width(); canvas.drawText(headerRirhtText, right-rightTextWidth-bitmap.getWidth()-textPaddingRght, viewTop - headerHeight / 2 + txtYAxis, mHeaderTxtPaint); } canvas.drawBitmap(bitmap,right-bitmap.getWidth()-bitmapPadding,viewTop -headerHeight / 2 - bitmap.getHeight()/2,mBitmapPaint); canvas.drawRect(left, viewTop, right, viewTop+bottomLineHeight, mBottomLinePaint); } if (headerHeight < viewTop && viewTop <= 2 * headerHeight) { //此判斷是剛好2個頭部碰撞,懸浮頭部就要偏移 translateTop = viewTop - 2 * headerHeight; } stickyHeaderPosArray.put(pos, viewTop);//將頭部資訊放進array Log.i(TAG, "繪製各個頭部" + pos); } } if (firstHeaderName == null) return; canvas.save(); canvas.translate(0, translateTop); if (headerDrawEvent != null) {//inflater View headerView; if (headViewMap.get(firstPos) == null) { headerView = headerDrawEvent.getHeaderView(firstPos); headerView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); headerView.setDrawingCacheEnabled(true); headerView.layout(0, 0, right, headerHeight);//佈局layout headViewMap.put(firstPos, headerView); canvas.drawBitmap(headerView.getDrawingCache(), left, 0, null); } else { headerView = headViewMap.get(firstPos); canvas.drawBitmap(headerView.getDrawingCache(), left, 0, null); } } else { /*繪製懸浮的頭部*/ canvas.drawRect(left, 0, right, headerHeight, mHeaderContentPaint); canvas.drawText(firstHeaderName, left + textPaddingLeft, headerHeight / 2 + txtYAxis, mHeaderTxtPaint); if (!TextUtils.isEmpty(headerRirhtText)){ Rect mBounds = new Rect(); mHeaderTxtPaint.getTextBounds(headerRirhtText,0,headerRirhtText.length(),mBounds); float rightTextWidth=mBounds.width(); canvas.drawText(headerRirhtText, right-rightTextWidth-bitmap.getWidth()-textPaddingRght, headerHeight / 2 + txtYAxis, mHeaderTxtPaint); } canvas.drawBitmap(bitmap,right-bitmap.getWidth()-bitmapPadding,headerHeight / 2 - bitmap.getHeight()/2,mBitmapPaint); canvas.drawRect(left, headerHeight, right, headerHeight+bottomLineHeight, mBottomLinePaint); } canvas.restore(); Log.i(TAG, "繪製懸浮頭部"); } private Map<Integer, View> headViewMap = new HashMap<>(); public interface OnHeaderClickListener { void headerClick(int pos); } private OnHeaderClickListener headerClickEvent; public void setOnHeaderClickListener(OnHeaderClickListener headerClickListener) { this.headerClickEvent = headerClickListener; } private GestureDetector.OnGestureListener gestureListener = new GestureDetector.OnGestureListener() { @Override public boolean onDown(MotionEvent e) { return false; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { for (int i = 0; i < stickyHeaderPosArray.size(); i++) { int value = stickyHeaderPosArray.valueAt(i); LogUtil.e("value"+value); float y = e.getY(); if (value - headerHeight <= y && y <= value) {//如果點選到分組頭 if (headerClickEvent != null) { headerClickEvent.headerClick(stickyHeaderPosArray.keyAt(i)); LogUtil.e("stickyHeaderPosArray.keyAt(i)"+stickyHeaderPosArray.keyAt(i)); } return true; } } return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } }; private OnDecorationHeadDraw headerDrawEvent; public interface OnDecorationHeadDraw { View getHeaderView(int pos); } /** * 只是用來繪製,不能做其他處理/點選事件等 */ public void setOnDecorationHeadDraw(OnDecorationHeadDraw decorationHeadDraw) { this.headerDrawEvent = decorationHeadDraw; } private Map<String, Drawable> imgDrawableMap = new HashMap<>(); private Drawable getImg(String url) { return imgDrawableMap.get(url); } public void onDestory() { headViewMap.clear(); imgDrawableMap.clear(); stickyHeaderPosArray.clear(); mRecyclerView = null; setOnHeaderClickListener(null); setOnDecorationHeadDraw(null); } public void setHeaderHeight(int headerHeight) { this.headerHeight = headerHeight; } public void setTextPaddingLeft(int textPaddingLeft) { this.textPaddingLeft = textPaddingLeft; } public void setTextSize(int textSize) { this.textSize = textSize; this.mHeaderTxtPaint.setTextSize(textSize); } public void setTextColor(int textColor) { this.textColor = textColor; this.mHeaderTxtPaint.setColor(textColor); } public void setHeaderContentColor(int headerContentColor) { this.headerContentColor = headerContentColor; this.mHeaderContentPaint.setColor(headerContentColor); } public void setBottomLineHeight(int bottomLineHeight) { this.bottomLineHeight = bottomLineHeight; } public void setBottomLineColor(int bottomLineColor) { this.bottomLineColor = bottomLineColor; this.mBottomLinePaint.setColor(bottomLineColor); } }
2.使用和原來的一樣,但是滑動後頭部點選沒反應
https://github.com/timehop/sticky-headers-recyclerview
這個很方便,懸浮頭部也可點選,但是點選返回不對,解決辦法:直接判斷position是否等於0,等於0就返回recycleView的當前顯示的第一個item的位置,這樣就可以正確獲取位置了
StickyRecyclerHeadersTouchListener touchListener = new StickyRecyclerHeadersTouchListener(recyclerView, headersDecor); touchListener.setOnHeaderClickListener( new StickyRecyclerHeadersTouchListener.OnHeaderClickListener() { @Override public void onHeaderClick(View header, int position, long headerId) { int itemPosition; if (position==0){ itemPosition=manager.findFirstVisibleItemPosition(); }else { itemPosition=position; } } }); recyclerView.addOnItemTouchListener(touchListener);