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()
- 后续事件将直接传递给B的onTouchEvent()处理
- 后续事件将不会再传递给B的onInterceptTouchEvent方法,该方法一旦返回一次true,就再也不会被调用了。
C再也不会收到该事件列产生的后续事件。
特别注意:
- 如果ViewGroup A 拦截了一个半路的事件(如MOVE),这个事件将会被系统变成一个CANCEL事件并传递给之前处理该事件的子View;
- 该事件不会再传递给ViewGroup A的onTouchEvent()
- 只有再到来的事件才会传递到ViewGroup A的onTouchEvent()