Android View 事件分发场景

01.事件分发背景描述

  • 讨论的布局层次如下:
    • 最外层:Activiy A,包含两个子View:ViewGroup B、View C
    • 中间层:ViewGroup B,包含一个子View:View C
    • 最内层:View C
  • 触摸情况
    • 假设用户首先触摸到屏幕上View C上的某个点(如图中黄色区域),那么Action_DOWN事件就在该点产生,然后用户移动手指并最后离开屏幕。

02.事件传递情况

  • 一般的事件传递场景有:
    • 默认情况
    • 处理事件
    • 拦截DOWN事件
    • 拦截后续事件(MOVE、UP)

2.1 默认情况

  • 即不对控件里的方法(dispatchTouchEvent()、onTouchEvent()、onInterceptTouchEvent())进行重写或更改返回值
  • 那么调用的是这3个方法的默认实现:调用父类的方法
  • 事件传递情况:
    • 从Activity A—->ViewGroup B—>View C,从上往下调用dispatchTouchEvent()
    • 再由View C—>ViewGroup B —>Activity A,从下往上调用onTouchEvent()
  • 注:虽然ViewGroup B的onInterceptTouchEvent方法对DOWN事件返回了false,后续的事件(MOVE、UP)依然会传递给它的onInterceptTouchEvent()
  • 注意:这一点与onTouchEvent的行为是不一样的。

2.2 处理事件

  • 假设View C希望处理这个点击事件,即C被设置成可点击的(Clickable)或者覆写了C的onTouchEvent方法返回true。
    • 最常见的:设置Button按钮来响应点击事件
  • 事件传递情况:
    • DOWN事件被传递给C的onTouchEvent方法,该方法返回true,表示处理这个事件
    • 因为C正在处理这个事件,那么DOWN事件将不再往上传递给B和A的onTouchEvent();
    • 该事件列的其他事件(Move、Up)也将传递给C的onTouchEvent()

2.3 拦截DOWN事件

  • 假设ViewGroup B希望处理这个点击事件,即B覆写了onInterceptTouchEvent()返回true、onTouchEvent()返回true。
  • 事件传递情况:
    • DOWN事件被传递给B的onInterceptTouchEvent()方法,该方法返回true,表示拦截这个事件,即自己处理这个事件(不再往下传递)
    • 调用onTouchEvent()处理事件(DOWN事件将不再往上传递给A的onTouchEvent())
    • 该事件列的其他事件(Move、Up)将直接传递给B的onTouchEvent()
  • 该事件列的其他事件(Move、Up)将不会再传递给B的onInterceptTouchEvent方法,该方法一旦返回一次true,就再也不会被调用了。

2.4 拦截DOWN的后续事件

  • 假设ViewGroup B没有拦截DOWN事件(还是View C来处理DOWN事件),但它拦截了接下来的MOVE事件。

    • DOWN事件传递到C的onTouchEvent方法,返回了true。

    • 在后续到来的MOVE事件,B的onInterceptTouchEvent方法返回true拦截该MOVE事件,但该事件并没有传递给B;这个MOVE事件将会被系统变成一个CANCEL事件传递给C的onTouchEvent方法

    • 后续又来了一个MOVE事件,该MOVE事件才会直接传递给B的onTouchEvent()

      1. 后续事件将直接传递给B的onTouchEvent()处理
      2. 后续事件将不会再传递给B的onInterceptTouchEvent方法,该方法一旦返回一次true,就再也不会被调用了。
    • C再也不会收到该事件列产生的后续事件。

  • 特别注意:

    • 如果ViewGroup A 拦截了一个半路的事件(如MOVE),这个事件将会被系统变成一个CANCEL事件并传递给之前处理该事件的子View;
    • 该事件不会再传递给ViewGroup A的onTouchEvent()
    • 只有再到来的事件才会传递到ViewGroup A的onTouchEvent()