仿QQ會話列表,左滑item的時候,展示出刪除等菜單。
把每個Item看成一個LinearLayout,它包含兩個子控件,一個是Item要顯示的內容,還有一個當然就是我們的右側菜單了
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="20dp"> <TextView android:id="@+id/tv_text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout> <LinearLayout android:layout_width="80dp" android:layout_height="match_parent"> <TextView android:id="@+id/tv_delete" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" android:background="@android:color/holo_red_light" android:gravity="center" android:text="刪除" android:textColor="@android:color/white"/> </LinearLayout> </LinearLayout>
接着將整個菜單的顯示與隱藏把封閉在ItemSlideHelper裏面,關鍵的代碼如下
@Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { Log.d(TAG, "onInterceptTouchEvent: " + e.getAction()); int action = MotionEventCompat.getActionMasked(e); int x = (int) e.getX(); int y = (int) e.getY(); //如果RecyclerView滾動狀態不是空閒targetView不是空 if(rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE){ if(mTargetView != null){ //隱藏已經打開 smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2); mTargetView = null; } return false; } //如果正在運行動畫 ,直接攔截什麼都不做 if(mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning()){ return true; } boolean needIntercept = false; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(e, 0); mLastX = (int) e.getX(); mLastY = (int) e.getY(); /* * 如果之前有一個已經打開的項目,當此次點擊事件沒有發生在右側的菜單中則返回TRUE, * 如果點擊的是右側菜單那麼返回FALSE這樣做的原因是因爲菜單需要響應Onclick * */ if(mTargetView != null){ return !inView(x, y); } //查找需要顯示菜單的view; mTargetView = mCallback.findTargetView(x, y); break; case MotionEvent.ACTION_MOVE: int deltaX = (x - mLastX); int deltaY = (y - mLastY); if(Math.abs(deltaY) > Math.abs(deltaX)) return false; //如果移動距離達到要求,則攔截 needIntercept = mIsDragging = mTargetView != null && Math.abs(deltaX) >= mTouchSlop; if(isExpanded()){ needIntercept = true; //摺疊菜單 smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2); mTargetView = null; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: /* * 走這是因爲沒有發生過攔截事件 * */ if(isExpanded()){ if (inView(x, y)) { // 如果走這那行這個ACTION_UP的事件會發生在右側的菜單中 Log.d(TAG, "click item"); }else{ //攔截事件,防止targetView執行onClick事件 needIntercept = true; } //摺疊菜單 smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2); } mTargetView = null; break; } return needIntercept ; }
@Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { Log.d(TAG, "onTouchEvent: " + e.getAction()); if(mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning() || mTargetView == null) return; //如果要響應fling事件設置將mIsDragging設爲false if (mGestureDetector.onTouchEvent(e)) { mIsDragging = false; return; } int x = (int) e.getX(); int y = (int) e.getY(); int action = MotionEventCompat.getActionMasked(e); switch (action) { case MotionEvent.ACTION_DOWN: //RecyclerView 不會轉發這個Down事件 break; case MotionEvent.ACTION_MOVE: int deltaX = (int) (mLastX - e.getX()); if(mIsDragging) { horizontalDrag(deltaX); } mLastX = x; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if(mIsDragging){ if(!smoothHorizontalExpandOrCollapse(0) && isCollapsed()) mTargetView = null; mIsDragging = false; } break; } }
ItemSlideHelper裏面有個接口,最後讓Adapter實現這個接口即可
public interface Callback { int getHorizontalRange(RecyclerView.ViewHolder holder); RecyclerView.ViewHolder getChildViewHolder(View childView); View findTargetView(float x, float y); }