美文网首页
Android 侧滑删除编辑

Android 侧滑删除编辑

作者: MrDom | 来源:发表于2017-07-26 23:54 被阅读0次

    第一次写简书,写的不好或有错误的地方请多多包涵,闲话不多说,先上两张效果图。

    Paste_Image.png Paste_Image.png

    实现此种效果,大致有两种方法,此效果主要是处理滑动事件冲突,第一是重写RecyclerView,重写onInterceptTouchEvent和onTouchEvent。第二种方法时重写RecyclerView的子View,主要也是重写这两个方法,如果是继承ViewGroup,那么要重写onMeasure和onLayout,如果是继承LinearLayout则只要重写事件分发的几个方法就好。这里采用的第二种方法,重写RecyclerView的子View。下面主要说说重写的核心方法:

    先看onInterceptTouchEvent方法:

    @Override
        public boolean onInterceptTouchEvent(MotionEvent e) {
    
            boolean consume = false;
            int x = (int) e.getX();
            int y = (int) e.getY();
            switch (e.getAction()){
    
                case MotionEvent.ACTION_DOWN:
                    //获取手指按下时的坐标
                    mDownX = (int) e.getX();
                    mDownY = (int) e.getY();
                    Log.d(TAG, "mDownX:"+ mDownX);
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.d(TAG, "onInterceptTouchEvent:ACTION_MOVE");
                    int deltaX = x - mLastX;
                    int deltaY = y - mLastY;
    
                    if(Math.abs(deltaX) > mTouchSlop || Math.abs(deltaY) > mTouchSlop){
                        //当y方向滑动距离小于x方向时,拦截事件,交给自己处理
                        if(Math.abs(deltaX) > Math.abs(deltaY)){
                            Log.d(TAG, "onInterceptTouchEvent");
                            //不允许父元素拦截事件
                            getParent().requestDisallowInterceptTouchEvent(true);
                            //此时拦截事件,直接调用onTouchEvent,传入onTouchEvent的ACTION_MOVE事件中,不再走onTouchEvent的ACTION_DOWN事件
                            consume = true;
                        }else {
                            getParent().requestDisallowInterceptTouchEvent(false);
                        }
                    }
    
                    break;
                case MotionEvent.ACTION_UP:
                    Log.d(TAG, "ACTION_UP");
                    break;
            }
            mLastX = x;
            mLastY = y;
            return consume;
        }
    

    RecyclerView继承的是ViewGroup,因此默认不拦截任何点击事件,

    //当我们重写子View时,必须在合适的时机调用此方法,此处是不允许父元素也就是RecyclerView拦截该事件,此方法时设置一个标记位,具体的可以看源码或者具体了解一下事件分发机制。
    getParent().requestDisallowInterceptTouchEvent(true);
    

    onInterceptTouchEvent主要的逻辑是拦截事件,具体一点,当我们在x轴滑动的距离大于y轴滑动的距离时,我们需要子View拦截事件,也就是不允许父元素拦截事件,即

    //不允许父元素拦截事件
    getParent().requestDisallowInterceptTouchEvent(true);
    //此时拦截事件,直接调用onTouchEvent,传入onTouchEvent的ACTION_MOVE事件中,不再走onTouchEvent的ACTION_DOWN事件
    consume = true;
    

    当我们完成具体的拦截逻辑后,具体的执行逻辑在onTouchEvent中执行,下面看看代码:

    @Override
        public boolean onTouchEvent(MotionEvent e) {
            switch (e.getAction()){
                case MotionEvent.ACTION_DOWN:
                    break;
                case MotionEvent.ACTION_MOVE:
                    mMoveX = (int) e.getX();
                    mMoveY = (int) e.getY();
                    Log.d(TAG, "mMoveX - mDownX:"+ (mMoveX - mDownX));
    
                    scrollBy(-(mMoveX - mDownX),0);
    
                    int scrollX = getScrollX();
                    if (scrollX > mItemLength) {
                        scrollTo(mItemLength, 0);
                        isOpen = true;
                    }
                    if (scrollX < 0) {
                        scrollTo(0, 0);
                        isOpen = false;
                    }
    
                    break;
                case MotionEvent.ACTION_UP:
                    //滑动超过一定距离时,自动关闭或开启menu
                    int upX = (int) getX();
                    if(Math.abs(getScrollX())>= mMenuWidth && getScrollX() > 0 || Math.abs(getScrollX())< mMenuWidth && getScrollX() < 0){
                        open();
                    }else if(Math.abs(getScrollX())>= mMenuWidth && getScrollX() < 0 || Math.abs(getScrollX())< mMenuWidth && getScrollX() > 0){
                        close();
                    }
                    break;
            }
            mDownX = mMoveX;
            mDownY = mMoveY;
            return true;
        }
    

    具体的执行落在onTouchEvent的ACTION_MOVE中执行,这里的代码也不复杂,最后在ACTION_UP时判断,当滑动超过一定距离时,松开手指可通过Scroller实现平缓滑动的效果。

    最后为了实现更好的效果,重写了RecyclerView的onInterceptTouchEvent方法:

     @Override
        public boolean onInterceptTouchEvent(MotionEvent e) {
    
            switch (e.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.d(TAG, "onInterceptTouchEvent: down");
                    int childCount = getChildCount();
                    for(int i=0;i<childCount;i++){
                        SlidingView slidingView = (SlidingView) getChildAt(i);
                        if(slidingView.isOpen){
                            slidingView.close();
                        }
                    }
                    break;
            }
            return super.onInterceptTouchEvent(e);
        }
    

    具体的效果是,当手指触发ACTION_DOWN事件时,如果有未关闭的子View,那么关闭它。

    最后一点要说明的是,在RecyclerView的Adapter中,对于Menu的点击事件采用的是onTouchListener:

    ((ViewHolder)holder).tvEdit.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if(event.getAction() == MotionEvent.ACTION_DOWN){
                        int position = holder.getAdapterPosition();
                        Log.d(TAG, "onTouch: edit:"+position);
                        if(onClickListenerEditOrDelete != null){
                            onClickListenerEditOrDelete.OnClickListenerEdit(position);
                        }
                        return true;
                    }
                    return true;
                }
            });
    

    因为OnTouchListener的优先级高于OnClickListener,当使用OnClickListener时,有时能触发点击事件有时不能,如有同学知道所以然还想好好请教一番。

    具体的实现逻辑就是这样,重点是处理各种事件的滑动冲突,带一点自定义View的基本使用,Scroller和View的滑动机制。demo还有许多不足的地方,还需要多多优化。

    具体代码下载

    相关文章

      网友评论

          本文标题:Android 侧滑删除编辑

          本文链接:https://www.haomeiwen.com/subject/nldakxtx.html