Android中View的事件冲突

仅限于自己学习的笔记,有不正确的地方望指正。

常见的滑动冲突的场景

  1. 外部滑动方向和内部滑动方向不一致。
  2. 外部滑动方向和内部滑动方向一致。
  3. 以上两种嵌套。

    场景1:主要是ViewPager和Fragment配合使用组成的滑动效果。主流应用几乎都是使用这个效果。在这种效果中,可以通过左右滑动来切换界面,而每个页面的内部往往有是一个ListView。本身这种情况下是有冲突的。但是ViewPager内部处理了这种冲突。因此我们使用时候无需关系这类问题。

    场景2:内外两层同时上下滑动或者内外两层同时左右。例如ScrollView嵌套RecycleView导致滑动卡顿的现象。

    场景3:外部是SlideMenu效果,然后内部有一个ViewPager,ViewPager每个界面是ListView。


滑动冲突的处理原则

  • 场景1:当用户左右滑动时候,需要让外部的View拦截点击事件,当用户上下滑动时,需要让内部View拦截点击事件。这样我们可以根据特征来区分到底是左右滑动还是上下滑动。(可以根据滑动的两点坐标区分到时是左右滑动还是上下滑动)
  • 场景2:可以根据业务规定,当处于某种状态时需要外部View滑动,当处于某种状态时需要内部的View滑动。
  • 场景3:结合以上两种情况综合分析,处理滑动冲突,按照一定的规则处理。

    Viewtouch事件分发过程中重要的三个函数

  • dispatchTouchEvent 负责touch事件的分发
  • onInterceptTouchEvent 负责拦截touch事件
  • onTouchEvent 最终处理touch事件

滑动冲突的解决方式

  1. 外部拦截法
    外部拦截法就是点击事件都先经过父容器的拦截处理,如果父容器需要拦截则拦截,反之不需要则不拦截。此方法符合View事件分发机制。
public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            intercepted = false;
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            if(父容器拦截的规则){
                intercepted=true;
            }else{
                intercepted=false;
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            intercepted = false;
            break;
        }
        default:
            break;
        }
        mLastXIntercept=x;
        mLastYIntercept=y;
        return intercepted;
    }

此方法只是父元素在ACTION_MOVE事件中根据条件判断是否拦截。其他事件则不需要处理。如果拦截了则下层子元素接收不到事件。
2. 内部拦截法
内部拦截法只是父元素不拦截事件,全部交给子元素处理事件。如果子元素需要拦截则拦截,否则交给父容器拦截。需要配合requestDisallowInterceptTouchEvent方法才能正常执行。

public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            parent.requestDisallowInterceptTouchEvent(true); //父布局不要拦截此事件
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX=x-mLastXIntercept;
            int deltaY=y=mLastYIntercept;
            if(父容器需要拦截的事件){
                parent.requestDisallowInterceptTouchEvent(false); //父布局需要要拦截此事件
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            intercepted = false;
            break;
        }
        default:
            break;
        }
        mLastXIntercept=x;
        mLastYIntercept=y;
        return super.dispathTouchEvent(event);
    }

当需要父元素拦截时则调用父容器的requestDisallowInterceptTouchEvent(false)方法。父元素要默认拦截除了ACTION_DOWN以外的其他事件否则所有的子元素无法收到事件。

 public boolean onInterceptTouchEvent(MotionEvent ev) {
      if(ev.getAction==MotionEvent.ACTION_DOWN){
         return false;
      }else{
         return true;
      }
    }

参考:Android开发艺术探讨

你可能感兴趣的:(View)