View 的事件分发(二)—— 源码分析 (dispatchTouchEvent)
2023-11-03 07:29:21
从 Activity 到 View 的事件传递之路
在上一篇文章《View 的事件分发(一)—— 分发流程》中,我们了解了事件从用户点击屏幕到屏幕中的控件响应操作的大致流程:Activity -> ViewGroup -> View。那么,Activity 是如何将事件传递给我们在 xml 中写的根视图的呢?
带着这个问题,我们来分析一下 dispatchTouchEvent 方法的源码。
dispatchTouchEvent 方法
dispatchTouchEvent 方法是 View 类中定义的一个重要方法,用于分发触摸事件。该方法的签名如下:
public boolean dispatchTouchEvent(MotionEvent ev) {
//省略部分代码
}
该方法接受一个 MotionEvent 对象作为参数,并返回一个布尔值。如果该方法返回 true,则表示事件已被处理;如果返回 false,则表示事件未被处理,并且会继续向父视图分发。
参数MotionEvent
MotionEvent 对象包含有关触摸事件的信息,例如触摸点的位置、触摸点的数量以及触摸事件的类型(如按下、移动、抬起等)。
返回值
dispatchTouchEvent 方法的返回值是一个布尔值。如果该方法返回 true,则表示事件已被处理;如果返回 false,则表示事件未被处理,并且会继续向父视图分发。
源码分析
为了更好地理解 dispatchTouchEvent 方法是如何工作的,我们来看一下它的源码。
public boolean dispatchTouchEvent(MotionEvent ev) {
//省略部分代码
// 1. 检查是否拦截事件
boolean intercepted = onInterceptTouchEvent(ev);
// 2. 如果拦截事件,则不分发事件
if (intercepted) {
return true;
}
// 3. 如果没有拦截事件,则分发事件
// 3.1 首先分发给当前视图
boolean handled = handleTouchEvent(ev);
// 3.2 如果当前视图没有处理事件,则分发给子视图
if (!handled) {
handled = dispatchTransformedTouchEvent(ev, true);
}
// 4. 如果子视图也没有处理事件,则将事件传递给父视图
if (!handled) {
handled = dispatchTouchEvent(ev);
}
//省略部分代码
return handled;
}
1. 检查是否拦截事件
在分发事件之前,View 会首先调用 onInterceptTouchEvent 方法来检查是否拦截事件。如果该方法返回 true,则表示 View 拦截了该事件,并且不会将事件分发给子视图。
2. 如果拦截事件,则不分发事件
如果 View 拦截了事件,则不会将事件分发给子视图。这是因为 View 已经决定自己处理该事件,不需要子视图的参与。
3. 如果没有拦截事件,则分发事件
如果 View 没有拦截事件,则会将事件分发给子视图。
3.1 首先分发给当前视图
View 会首先将事件分发给自己。如果 View 能够处理该事件,则会直接处理该事件,并且不会将事件分发给子视图。
3.2 如果当前视图没有处理事件,则分发给子视图
如果 View 不能处理该事件,则会将事件分发给子视图。View 会依次将事件分发给自己的所有子视图,直到有子视图能够处理该事件为止。
4. 如果子视图也没有处理事件,则将事件传递给父视图
如果所有的子视图都不能处理该事件,则 View 会将事件传递给自己的父视图。父视图会按照同样的流程分发事件,直到有 View 能够处理该事件为止。
总结
通过分析 dispatchTouchEvent 方法的源码,我们可以看到,View 的事件分发机制是一个非常复杂的过程。该机制确保了事件能够被正确地分发给能够处理该事件的 View,从而实现用户与界面之间的交互。