基于recyclerview的itemview左滑删除置顶功能(二)

改善用户体验(滑动)

在使用过QQ或者类似左滑功能的时候,都不会说滑到一半就停住了。必定会写一个逻辑判断用户滑动是否到达一定的值,假若到达则自动滑过去,若没有,则还原。在上一篇文章中,我们仅仅实现了滑动功能,只要随时放手,滑动的view都会停住。因此我们定义一个onSlide函数进行自动滑动

     /*
    * flag = 0代表正在滑动时的调用,以便在滑到指定值时自动滑动,flag = 1代表收回滑块
    * ObjectAnimator:ValueAnimator的子类,可以对一个object的属性进行值动画
    * */
    private boolean onSlide(int flag) {
        if (itemView == null){
            return false;
        }
        int scrollX = itemView.getScrollX();
        int scrollWidth = mviewWidth; //自定义view的长度
        if (manimator != null){
            return false;
        }
        int to = 0; //需要滑到的位置;
        int duration = 100; //滑动时间
        if (flag == 0) {
            if (scrollX >= scrollWidth/2){ //滑到大于一半,则自动滑完
                to = scrollWidth;
            }else { //否侧复原
                to = 0;
            }
        }else{
            to = 0;
        }
        manimator = ObjectAnimator.ofInt(itemView , "scrollX" , to); //一个ObjectAnimator的用法
        manimator.setDuration(duration);
        manimator.addListener(new Animator.AnimatorListener() { //动画监听
            @Override
            public void onAnimationStart(Animator animation) {
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                manimator = null;
                if (!isExposed()){
                    itemView = null;
                }
            }
            @Override
            public void onAnimationCancel(Animator animation) {
                manimator = null;
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        manimator.start();
        return true;
    }

接下来我们只要在onTouchEvent的MotionEvent.ACTION_UP事件判断中添加Slide(0)即可。


判断是否点击了自定义的view

在我们自定义了view后,应该如何判断并触发他的点击事件呢?我们编写一个函数isHit(int x, int y)根据点击的地方判断是否点中。

   /*
    * 判断是否点中
    * 根据itemView的属性建立一个矩形区域(Rect),并利用其的contains(int x , int y)函数判断
    * 是否在目标区域
    * */
    private boolean isHit(int x, int y) {
        if (itemView == null || !isExposed()){
            return false;
        }
        //这里需要左上角和右下角的点
        int top = itemView.getTop(); //左上角y坐标
        int bottom = itemView.getBottom(); //右下角y坐标
        int left = itemView.getWidth() - mviewWidth;
        int right = left + mviewWidth;
        Rect rect = new Rect(left , top , right , bottom);
        boolean isHit =  rect.contains(x , y);
        return isHit;
    }

这样就基本可以了。但随之而来有个问题。如果我要加第二个自定义的view呢,这里的矩形区域是写死的,也就是说难道我每次加的时候要算坐标改代码岂不是很麻烦。这个后面再说,就编写几个接口,然后通过接口的调用就可以解决这个麻烦。 这里用到了个isExposed()函数,用于判断当前是否view已经展开(滑出)

    /*
    * 判断是否滑出
    * */
    private boolean isExposed() {
        return itemView!= null  && itemView.getScrollX() == mviewWidth;
    }

添加自定义view监听

在只有一个view的情况下很简单就可以添加了,这里不用addListener这样的函数,而是直接在 onInterceptTouchEvent函数中MotionEvent.ACTION_DOWN中写代码即可

                if (isExposed() &&  isHit(firstX , firstY)  && itemView != null){
                    Log.d("good", "选中了 ");
                    itemView = null;
                }

这是不是很不面向对象。我也这么觉得。而且要是你又新添加了view,那又要大改特改,很麻烦。在第三篇中会对代码进行优化和重构。


事件拦截

在我们画出滑块之后,我们竟然发现还能在其他的itemView中继续滑多一个出来,太诡异了!而且,这时候如果对itemView添加了监听,也会触发。因此我们要使用OnInterceptTouchEvent进行指定拦截。

        //自定义view展开时,却点到了其他的地方
        if (isExposed() &&!isHit(x , y) && itemView != null){
            onSlide(1); //自定义view复原,滑回去
            itemView = null;
            return true; //拦截,防止触发itemView的监听
        }

接下来要拦截itemView的点击事件,什么时候是点击itemView,什么时候是滑动? 你要点击itemView,在OnInterceptTouchEvent函数中,就要返回false,不拦截。你要滑动,就要返回true进行拦截。因此我们要判断什么时候该拦截什么时候不该拦截。 方法就是利用一个isIntercept的布尔值判断。 因此比较完整的拦截函数:

     boolean isIntercept = false;
        switch (action){
            case MotionEvent.ACTION_DOWN:
                InitclickItem(x , y); //初始化调用mcallback后的值
                firstX = x;
                firstY = y;
                if (isExposed() &&  isHit(firstX , firstY)  && itemView != null){
                    onSlide(1);
                    Log.d("good", "选中了 ");
                    itemView = null;
                    isIntercept = true; //拦截,不让事件分发下去,以免点击到item
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = firstX - x;
                int delteY= firstY - y;
                isIntercept = deltaX >= mTouchSlop;
                if (Math.abs(deltaX) < Math.abs(delteY)){ // 滑动recyclerview
                    return false;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return isIntercept;
    }

总结

1.改善用户体验 2.判断是否点击到了目标区域 3.为点击添加事件 4.事件拦截