美文网首页Android知识点点滴汇聚
最简单易学的RecycleView左滑删除效果(ViewDrag

最简单易学的RecycleView左滑删除效果(ViewDrag

作者: 幸福的程序媛 | 来源:发表于2017-11-30 16:28 被阅读58次

    项目中用到RecycleView左滑删除ui及功能效果。自己写了一个,感觉很简单易懂的,现分享一下。
    效果图


    QQ图片20171130160152.png

    ViewDragHelper是一个方便移动子控件的辅助类,特别方便。
    初始化ViewDragHelper

        public DragLayout(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
        }
    

    要想达到子View滑动效果,记得将触摸事件交给ViewDragHelper处理

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
          final int action = MotionEventCompat.getActionMasked(ev);
          if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
              mDragHelper.cancel();
              return false;
          }
          return mDragHelper.shouldInterceptTouchEvent(ev);
        }
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
          mDragHelper.processTouchEvent(ev);
          return true;
        }
    

    要想实现滑动删除效果,考虑RecylceView适配器里面的item是个FrameLayout,滑动之前的View显示在最上面,下面是删除按钮,被上面view遮住了,所以你看不到。当上面view向左移动时就会将删除按钮显露出来了,于是实现了滑动删除效果。可以限定移动范围刚好是下面删除按钮的宽度,这样上面的view就不会移动出边界。
    指定谁可以滑动

      @Override
                public boolean tryCaptureView(View child, int pointerId) {
                    return child == dragView;//出入你想要谁可移动
                }
    

    最重要的逻辑就是限定移动左边界的范围。

     @Override
                public int clampViewPositionHorizontal(View child, int left, int dx) {
    
                    mLeft = 0;
                    if (left < 0 && Math.abs(left) > maxLeftPointReverse) { //限定向左最大到达位置
                        mLeft = -maxLeftPointReverse;
                        return mLeft;
                    }
                    if (left > mInitX) { //设置向右最大的left置
                        mLeft = mInitX;
                        return mLeft;
                    }
                    mLeft = left;
    
                    return mLeft;
                }
    

    该方法返回的是移动view的左边位置。
    注意2个边界值,注释都写清楚了。
    学习两个方法:

    viewDragHelper.settleCapturedViewAt(mInitX, mInitTop);
    

    作用像方法名字一样,该方法只能在onViewReleased方法里面调用,否则会报错。该方法会将view平滑滑动到指定位置。
    我们的效果是点击删除按钮,如果该项不可删除,则回到初始状态,删除按钮隐藏。这时用

    viewDragHelper.smoothSlideViewTo(dragView, mInitX, mInitTop);
    

    将view滑动回去。
    另外,在RecycleView上下滑动时,已处于打开状态的item会自动回到关闭状态,删除按钮被遮住了。要想解决这个问题,我是在adapter里面给item设置tag的方式,当打开时tag为数据源,关闭时tag为空,当判断为打开过的item时再将其smoothSlideViewTo滑动到打开状态,否则滑动到初始状态。后来发现这样做依然不行,因为recycleView的回收机制,导致滑动时onBindViewHolder走不到,所以即使这样设置了也没有用,后来找到解决方案,需要设置recycleView.setItemViewCacheSize(0)才能保证onBindViewHolder走得到,这里要注意一下

     if (orderTravelViewHolder.swipeLayout.getTag()!=null&&((OrderList_Bean)orderTravelViewHolder.swipeLayout.getTag()).equals(orderInfo)){
                        orderTravelViewHolder.swipeLayout.toOpen();
                }else{
                    orderTravelViewHolder.swipeLayout.toNormal();
                }
    

    另外item的水平滑动事件可能会引起RecycleView的滑动事件,所以也要处理一下。
    如下:

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
    
    
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downX = ev.getRawX();
                    downY = ev.getRawY();
    
                    break;
                case MotionEvent.ACTION_MOVE:
                    float disX = ev.getRawX() - downX;
                    float disY = ev.getRawY() - downY;
                    ViewParent viewGroup = getParent();
    
                    if (Math.abs(disX) > Math.abs(disY)) {
                        viewGroup.requestDisallowInterceptTouchEvent(true);//水平滑动时请求reycleView不要拦截事件
                    } else {
                        viewGroup.requestDisallowInterceptTouchEvent(false);
                    }
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    viewDragHelper.cancel();
                    break;
            }
            if (canSwipe) {
                return viewDragHelper.shouldInterceptTouchEvent(ev);
            } else {
                return super.onInterceptTouchEvent(ev);
            }
    
        }
    

    完整代码如此

    public class SwipeLayout extends FrameLayout {
    
        ViewDragHelper viewDragHelper;
    
        ViewDragHelper.Callback callback;
        View dragView;
        TextView hideView; //隐藏在下面的view
    
        int maxLeftPointReverse; //左边所能到达的最左边的距离的绝对值
    
    
        boolean canSwipe = true; //是否可以滑动删除
        int mHorizontalDragRange; //水平可以拖动的距离最大范围
        Function<Void, Void> animOpenFunction;//打开后的回调
        Function<Void, Void> animCloseFunction;//关闭后的回调
        float downX = 0, downY = 0;
        private int mInitX;
        private int mLeft;
        private int mInitTop;
    
        public SwipeLayout(@NonNull Context context) {
            super(context);
            init(context);
        }
    
        public SwipeLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init(context);
    
        }
    
        public SwipeLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
    
        }
    
        public void setCanSwipe(boolean canSwipe) {
            this.canSwipe = canSwipe;
        }
    
        public void setAnimCloseFunction(Function<Void, Void> animCloseFunction) {
            this.animCloseFunction = animCloseFunction;
        }
    
        public void setAnimOpenFunction(Function<Void, Void> animOpenFunction) {
            this.animOpenFunction = animOpenFunction;
        }
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            if (getChildCount() == 2) {
                hideView = (TextView) getChildAt(0);
                dragView = getChildAt(1);
                hideView.post(new Runnable() {
                    @Override
                    public void run() {
    
                        mHorizontalDragRange = hideView.getWidth();
    
    
                    }
                });
                dragView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
                    @Override
                    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                        mInitX = (int) dragView.getX();
                        mInitTop = dragView.getTop();
                        maxLeftPointReverse = hideView.getWidth() - mInitX;
                        dragView.removeOnLayoutChangeListener(this);
                    }
                });
    
            }
        }
    
        public void swipeToNormal() {
            dragView.post(new Runnable() {
                @Override
                public void run() {
    
                    if (dragView.getX() != mInitX) {
                        boolean result = viewDragHelper.smoothSlideViewTo(dragView, mInitX, mInitTop);
                        LogUtil.d("-->result is " + result);
                        ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
    
                    }
                }
            });
    
        }
    
        public void toOpen() {
    
            dragView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    if (dragView.getX() != -maxLeftPointReverse) {
                        viewDragHelper.smoothSlideViewTo(dragView, -maxLeftPointReverse, mInitTop);
                    }
                    dragView.removeOnLayoutChangeListener(this);
                }
            });
    
        }
    
        public void toNormal() {
            dragView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    if (dragView.getX() != mInitX) {
                        viewDragHelper.smoothSlideViewTo(dragView, mInitX, mInitTop);
    
                    }
                    dragView.removeOnLayoutChangeListener(this);
                }
            });
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
    
            if (canSwipe) {
                viewDragHelper.processTouchEvent(ev);
                return true;
            } else {
                return super.onTouchEvent(ev);
            }
    
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
    
    
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downX = ev.getRawX();
                    downY = ev.getRawY();
    
                    break;
                case MotionEvent.ACTION_MOVE:
                    float disX = ev.getRawX() - downX;
                    float disY = ev.getRawY() - downY;
                    ViewParent viewGroup = getParent();
    
                    if (Math.abs(disX) > Math.abs(disY)) {
                        viewGroup.requestDisallowInterceptTouchEvent(true);//水平滑动时请求reycleView不要拦截事件
                    } else {
                        viewGroup.requestDisallowInterceptTouchEvent(false);
                    }
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    viewDragHelper.cancel();
                    break;
            }
            if (canSwipe) {
                return viewDragHelper.shouldInterceptTouchEvent(ev);
            } else {
                return super.onInterceptTouchEvent(ev);
            }
    
        }
    
    
        private void init(Context context) {
            callback = new ViewDragHelper.Callback() {
                @Override
                public boolean tryCaptureView(View child, int pointerId) {
                    return child == dragView;
                }
    
                @Override
                public int clampViewPositionHorizontal(View child, int left, int dx) {
    
                    mLeft = 0;
                    if (left < 0 && Math.abs(left) > maxLeftPointReverse) { //限定向左最大到达位置
                        mLeft = -maxLeftPointReverse;
                        return mLeft;
                    }
                    if (left > mInitX) { //设置向右最大位置
                        mLeft = mInitX;
                        return mLeft;
                    }
                    mLeft = left;
    
                    return mLeft;
                }
    
                @Override
                public int clampViewPositionVertical(View child, int top, int dy) {
                    LogUtil.d("-->top is " + top);
                    return mInitTop;
                }
    
                @Override
                public void onEdgeTouched(int edgeFlags, int pointerId) {
                    super.onEdgeTouched(edgeFlags, pointerId);
                }
    
                @Override
                public int getViewHorizontalDragRange(View child) {
                    return mHorizontalDragRange;
                }
    
                @Override
                public void onViewReleased(View releasedChild, float xvel, float yvel) {
                    super.onViewReleased(releasedChild, xvel, yvel);
                    if (Math.abs(mInitX - mLeft) >= mHorizontalDragRange / 3) {//open
    
                        if (viewDragHelper.settleCapturedViewAt(-maxLeftPointReverse, mInitTop)) {
                            ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
    
                        }
    
                        if (animOpenFunction != null) {
                            try {
                                animOpenFunction.apply(null);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    } else {//close
                        if (viewDragHelper.settleCapturedViewAt(mInitX, mInitTop)) {
                            ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
    
                        }
                        if (animCloseFunction != null) {
                            try {
                                animCloseFunction.apply(null);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
    
    
                    }
                }
    
            };
    
            viewDragHelper = ViewDragHelper.create(this, 1.0f, callback);
    
    
        }
    
    
        @Override
        public void computeScroll() {
            if (viewDragHelper.continueSettling(true)) {
                ViewCompat.postInvalidateOnAnimation(this);
            }
        }
    
    
    }
    
    

    特别鸣谢:
    http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0911/1680.html
    https://www.cnblogs.com/epilogue/p/7723482.html
    如果你觉得对你有一丁点帮助,欢迎给点个爱心,谢谢!

    相关文章

      网友评论

      本文标题:最简单易学的RecycleView左滑删除效果(ViewDrag

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