仿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); }