最近項目有需要弄一個可以像手機QQ會話頁一樣可以滑動的小菜單,每一個Item當用戶在向左滑動的時候右側會出現一個小菜單當時就想在也不是很難心想着找個開源的使用就好呢,但是我的項目是用的RecyclerView網上基本沒有類似的沒辦法只能自己弄一個。
先說一說我的實現原理我把每個Item看成一個LinearLayout它包含兩個子控件一個是Item要顯示的內容還有一個當然就是我們的右側菜單了,這個頂部LinearLayout就是包含內容與菜單的容器,通過滾動這個容器的scrollX來顯示我們的菜單,原理還是不難的,我把整個菜單的顯示與隱藏把封閉在ItemSlideHelper里面這樣想換一種方法實現也不用去別的代碼很方便,當然ItemSlidHelper是要實現RecyclerView.OnItemTouchListener的因為我們的基本操作都是基於Touch事件的,有興趣的可以看看,有BUG可以回復我。
關於RecyclerView.OnItemTouchListener的幾個方法我也學習了下也不是很難主要是攔截與操作這兩個東西一定要配合好,還有就是RecyclerView的滾動狀態,因為在RecyclerVIew滾動的時候我們的滑動菜單是不能操作的不然就會產生混亂,在項目開發的時候由於我的Item是有onClick事件的,那么在用戶滑出菜單的時候也要把onClick事件給攔截但是又不能攔截菜單的Onclick事件我是通過容器的rect與scrollX的偏移來解決這個問題的可以看源碼就知道呢。下面是攔截代碼。
@Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { if(!mCallback.isEnable()) return false; int action = MotionEventCompat.getActionMasked(e); int x = (int) e.getX(); int y = (int) e.getY(); /* * 當我們沒有發生drag事件的時候cancel或up事件會發生interceptTouchEvent里面,如果TargetView等於空的時候直接 * 返回false,不攔截事件 * */ if(action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) if(mTargetView == null) return false; boolean needIntercept = false; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(e, 0); mLastX = (int) e.getX(); mLastY = (int) e.getY(); //查找需要顯示菜單的view; mTargetView = mCallback.findTargetView(x, y); /* * 如果正在動畫則攔截事件 * */ if (mExpandAndCollapseAnim != null) { //mExpandAndCollapseAnim.cancel(); mExpandAndCollapseAnim = null; needIntercept = true; } 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; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: /* * 當一個up事件發生在正常的范圍內且scrollX等於scrollRange則折疊view並攔截UP事件 * 防止view響應點擊事件 * */ if(isExpanded()){ if (inView(x, y)) { //攔截事件,防止targetView執行onClick事件 needIntercept = true; }else{ //如果走這那行這個ACTION_UP的事件會發生在右側的菜單中 } //折疊菜單 mTargetView.setScrollX(0); } dispatchCollapsedOrExpanded(); break; } return needIntercept && mTargetView != null; }
對於滾動和動畫的一些操作就比較簡單了,沒有DOWN事件是因為RecyclerView會在轉發這個事件的事件故意不轉發的可以看RecyclerViewr的dispatchOnItemTouch方法就知道了,在MOVE里面直接算一下移動的距離之類的就好了,通過deltaX來滾動容器的scrollX這樣就可以實現在拖動時候顯示或隱藏右側的菜單。
@Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { //如果要響應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()); horizontalDrag(deltaX); mLastX = x; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if(mIsDragging){ smoothHorizontalExpandOrCollapse(0); } dispatchCollapsedOrExpanded(); mIsDragging = false; break; } }
在使用的時候一定要調用下面代碼將ItemslideHelper與RecyclerView關聯起來.
mRecyclerView.addOnItemTouchListener(new ItemSlideHelper(mRecyclerView.getContext(), this));
項目我已經傳到Github上了
https://github.com/yjwfn/slideitem.git