Android事件分发

Android开发中常常是多个View嵌套使用, 那么如果点击了一个View产生的触摸事件由谁来负责响应和处理呢? 默认是由最底层的子View处理事件, 但是如果你想要的是父View处理怎么办呢? 这也就引发了事件冲突的Bug.

事件理解

事件处理分类

Activity和View都有对应触摸事件的相关方法, 对于触摸事件的处理分为以下三种:

类型 相关方法 Activity ViewGroup View
事件分发 dispatchTouchEvent
事件拦截 onInterceptTouchEvent X X
事件消费 onTouchEvent
  1. Activity不会拦截事件, 因为Activity拦截事件那是没有任何意义的, 他拦截了会导致整个屏幕都没有触摸事件的响应.
  2. View也不需要拦截事件, 因为View不是ViewGroup不可能存在子View来和当前的View产生冲突.

Android对于事件的处理

Android默认对于触摸事件都会传给自己的Children(下一级). 由Children判断是否消费事件, 消费事件的话返回true即不再对Children的Children进行事件分发了.

MotionEvent

事件 简介
ACTION_DOWN 手指 初次接触到屏幕 时触发。
ACTION_MOVE 手指 在屏幕上滑动 时触发,会会多次触发。单击并不会触发
ACTION_UP 手指 离开屏幕 时触发。
ACTION_CANCEL 事件 被上层拦截 时触发。

分发

1
boolean dispatchTouchEvent (MotionEvent event)

触摸一个控件就一定会执行其dispatchTouchEvent(), 不过是先从其树状视图的根节点父容器的dispatchTouchEvent()开始执行直到自身的dispatchTouchEvent().

Activity的分发

如果想要消费掉这个事件返回true. 底下的

1
2
3
4
5
6
7
8
9
10
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
// 屏幕事件的分发, 即点中的是非RootView的内容
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev); // 执行事件消费
}

拦截

1
boolean onInterceptTouchEvent (MotionEvent ev)

该方法只有ViewGroup才拥有, 用于拦截事件, 禁用被ChildView接收到事件的分发. ChildView就不会响应任何事件.

消费

1
boolean onTouchEvent (MotionEvent event)

从源码可以看出设置触摸事件监听器后, 会消费掉事件, 停止事件的传递.

1
2
3
4
5
6
7
mActivityMain.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Toast.makeText(MainActivity.this, "触摸事件", Toast.LENGTH_SHORT).show();
return true;
}
});

如果返回true, setOnClickListeneronTouchEvent()都将得不到任何事件. 无响应