1. 外部拦截法

    即指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要此事件就不拦截,这样就可以解决滑动冲突的问题,这种方法比较符合点击事件的分发机制。外部拦截法要重写父容器的onInterceptTouchEvent方法,并在内部做相应的拦截。伪代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean intercepted = false;
    int x = (int) ev.getX();
    int y = (int) ev.getY();
    switch (ev.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_DOWN事件返回false,因为父容器拦截了down事件,后续的action_move和action_up事件都会直接 交由父容器处理,事件无法传递给子元素.
​ ACTION_MOVE事件,可以根据需要来决定是否拦截,如果父容器拦截就返回true,不拦截就返回false。
​ ACTION_UP事件,必须返回false,因为up事件本身并没有多大的意义。

考虑一种情况,假设事件交由子元素处理,如果父容器在ACTION_UP时返回了true,就会导致子元素无法接收到ACTION_UP事件,这个时候子元素中的onclick事件就无法触发,但是父容器比较特殊,一旦它开始拦截任何一个事件,那么后续的事件都会交由它来处理,而ACTION_UP作为最后一个事件也必定可以传递给父容器,即使父容器的onInterceptTouchEvent方法在ACTION_UP时返回了false.

  1. 内部拦截法

    内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理,这种方法和android中的事件分发机制就不一样,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来比外部拦截法稍显复杂。伪代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Override
    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 - mLastX;
    int deltaY = y - mLastY;
    if (父容器需要此类点击事件) {
    parent.requestDisallowInterceptTouchEvent(false);
    }
    break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_CANCEL:
    break;
    default:
    break;
    }
    mLastX = x;
    mLastY = y;
    return super.dispatchTouchEvent(event);
    }

    上述代码是内部拦截法的典型代码,当面对不同的滑动策略时只需要修改里面1的条件即可,其他不需要做改动也不需要做改动。除了子元素需要做处理以外,父元素也要默认拦截除了ACTION_DOWN以外的其他事件,这样当子元素调用parent.requestDisallowInterceptTouchEvent(false)方法时,父元素才能继续拦截所需的事件。

    为什么父容器不能拦截ACTION_DOWN事件呢,因为这个事件不受FLAG_DISALLOW_INTERCEPT这个标记位的控制,所以一旦父容器拦截ACTION_DOWN事件了,那么所有的事件都无法传递到子元素去,这样内部拦截法就无法起作用了。

    父元素的修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

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