美文网首页
Touch事件传递与滑动冲突解决方案

Touch事件传递与滑动冲突解决方案

作者: 出云月 | 来源:发表于2017-08-31 14:04 被阅读0次

    一.当一个触摸事件产生后,它的传递过程顺序如下:Activity -> Window -> DecorView,即事件总是先传递给Activity,Activity再传递给Window,最后Window再传递给顶层View DecorView;然后在不被拦截的情况下,触摸事件会被传递到触摸位置对应的最底层View。传递完成后就要处理触摸事件了,处理顺序是从最底层View向Activity进行的。

    这里写图片描述

    跟Touch事件有关的处理方法主要由三个:
    //分派事件
    public boolean dispatchTouchEvent(MotionEvent ev)
    //拦截事件 只有ViewGroup才具有该方法
    public boolean onInterceptTouchEvent(MotionEvent ev)
    //处理事件
    public boolean onTouchEvent(MotionEvent event)

    为了能够方便理解事件分发的流程,我们设计一个实例,实例布局如图所示
    1.如果所有的View都不处理事件(onTouchEvent方法返回false),整个事件分发流程对应如图所示:


    这里写图片描述

    2.如果把ViewGroupA或者ViewGroupB的onInterceptTouchEvent()方法返回true,即拦截事件,事件分发过程如下所示:


    这里写图片描述 这里写图片描述

    某个View一旦决定拦截,那么这一个事件序列都只能由它处理(如果事件序列能够传递给它的话),并且它的onInterceptTouchEvent()不会再被调用。也就是说当一个View决定拦截一个事件后,那么系统会把同一个事件序列内的其他事件都直接交给它来处理,因此就不用再调用这个View的onInterceptTouchEvent()去询问它是否要拦截了。

    二.常见的滑动冲突场景
    常见的滑动冲突的场景可以分为如下三种:
    场景1 --- 外部滑动方向和内部滑动方向不一致
    场景2 --- 外部滑动方向和内部滑动方向一致
    场景3 --- 上面两种情况的嵌套

    这里写图片描述

    1.外部拦截法
    所谓外部拦截法是指所有的触摸事件都会先经过经过父容器的传递,从而父容器在需要此触摸事件的时候就可以拦截此触摸事件,否者就传递给子View。这样就可以解决滑动冲突的问题,这种方法比较符合触摸事件的传递、处理机制。外部拦截法需要重写父容器的onInterceptTouchEvent方法,在该方法中根据滑动冲突处理规则做相应的拦截即可,这种方法的典型代码如下:

    @Override
    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;
    }
    

    2.内部拦截法
    内部拦截法是指父容器不拦截任何触摸事件,所有的触摸事件都传递给子元素,如果子元素需要此触摸事件就直接消耗掉,否者就交由父容器进行处理,这种方法和Android中的事件传递、处理机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来较外部拦截法稍显复杂。这种方法需要重写子元素的dispatchTouchEvent方法和父容器的onInterceptTouchEvent方法,这种方法的典型代码如下:

    子元素的dispatchTouchEvent方法
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
     int x = (int) event.getX();
     int y = (int) event.getY();
    
     switch (event.getAction()) {
     case MotionEvent.ACTION_DOWN: {
         getParent().requestDisallowInterceptTouchEvent(true);
         break;
     }
     case MotionEvent.ACTION_MOVE: {
         int deltaX = x - mLastX;
         int deltaY = y - mLastY;
         if (父容器需要当前触摸事件) {
             getParent().requestDisallowInterceptTouchEvent(false);
         }
         break;
     }
     case MotionEvent.ACTION_UP: {
         break;
     }
     default:
         break;
     }
    
     mLastX = x;
     mLastY = y;
     return super.dispatchTouchEvent(event);
    }
    父容器的onInterceptTouchEvent方法
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
     int action = event.getAction();
     if (action == MotionEvent.ACTION_DOWN) {
         return false;
     } else {
         return true;
     }
    }
    

    相关文章

      网友评论

          本文标题:Touch事件传递与滑动冲突解决方案

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