Android 事件分发过程的源码主要分为四大过程:
- Activity 对点击事件的处理过程
- Window 对点击事件的处理过程
- 顶级 ViewGroup 对点击事件的处理过程
- View 对点击事件的处理过程
Activity 对点击事件的处理过程
当一个点击事件产生后,事件先传递给当前的 Activity,由 Activity 的 dispatchTouchEvent 方法进行事件派发。在内部具体的工作是由 Window 来完成的,Activity 将事件传递给了 Window。
1 | public boolean dispatchTouchEvent(MotionEvent ev) { |
Window 对点击事件的处理过程
Window 是个抽象类,它的唯一实现类是 PhoneWindow,PhoneWindow 通过 superDispatchTouchEvent 方法内部调用 DecorView 的 superDispatchTouchEvent 方法将事件传递给 DecorView。由于 DecorView 继承自 FrameLayout,它是我们 Activity 布局的容器,是顶级的父 View,所以最终事件会传递给顶级的根 View,即我们通过 setContentView 设置的 View,一般来说都是 ViewGroup。
1 | public boolean superDispatchTouchEvent(MotionEvent event) { |
顶级 ViewGroup 对点击事件的处理过程
ViewGroup 接收到事件后的处理过程主要分为两大部分:
- ViewGroup 对 ACTION_DOWN 事件的处理过程
- ViewGroup 对非 ACTION_DOWN 事件的处理过程
ViewGroup 对 ACTION_DOWN 事件的处理过程
- ViewGroup 先判断是否是 ACTION_DOWN 事件,如果是 DOWN 事件,则将 target 置空,接下来会调用 ViewGroup 的 onInterceptTouchEvent 方法判断 ViewGroup 是否要拦截这个事件,如果拦截,则后续的 ACTION_MOVE、ACTION_UP 都由 ViewGroup 处理,不会传递给子 View,并且 ViewGroup 的 onInterceptTouchEvent 不会再被调用。(因为ViewGroup已经知道自己要拦截这次事件了,就不需要再次判断了)
- 如 ViewGroup 的 onInterceptTouchEvent 返回 false,则表示 ViewGroup 不拦截此次 DOWN 事件,接下来会调用子 View 的 dispatchTouchEvent 方法继续往下分发,如果子 View 处理该事件,则将子 View 标记为处理该事件的 target,并且子 View 的 dispatchTouchEvent 方法返回 true。如果子 View 也不处理,则调用 super.dispatchTouchEvent 方法往上返回给父 View。
ViewGroup 对非 ACTION_DOWN 事件的处理过程
先判断有无 target(即 target 是否为空),如果 target 为空,则 ViewGroup 的 dispatchTouchEvent 方法返回 true,说明 ViewGroup 处理后续所有的非 DOWN 事件。
如果 target 不为空,则判断 disallowIntercept 禁止拦截标志,如果 disallowIntercept = true,说明子 View 设置了 FLAG_DISALLOW_INTERCEPT 标志禁止 ViewGroup 拦截非 DOWN 事件(当然前提是 ViewGroup 不拦截 DOWN 事件),则会调用 target.dispatchTouchEvent 方法,交给子 View 去往下分发。
如果 disallowIntercept = false,说明子 View 不禁止 ViewGroup 去拦截非 DOWN 事件,则 ViewGroup 会调用自己的 onInterceptTouchEvent 方法进一步判断自己是否需要拦截。
如果 ViewGroup 拦截说明 ViewGroup 要拦截此次非 DOWN 事件,则会将 target 置空,ViewGroup 的 onInterceptTouchEvent 方法返回 true,后续的事件都会交给 ViewGroup 去处理,不会传给子 View,onInterceptTouchEvent 也不会再次调用。
如果 ViewGroup 不拦截,则会调用 target.dispatchTouchEvent 方法继续往下分发,由 target.dispatchTouchEvent 方法确定返回值。
View 对事件的处理过程
如果子 View 不处理事件,则会调用 super.dispatchTouchEvent 方法将事件往回传给父 View。
如果子 View 处理该事件,则会先判断是否设置了 onTouchListener 监听器,如果设置了 onTouchListener,则会回调其 onTouch 方法,并且 onTouchEvent 方法将不会被调用。
如果没设置 onTouchListener,则会调用 onTouchEvent 方法,在 onTouchEvent 方法内部会判断是否设置了 onClickListener 监听器,如果设置了则会回调 onClickListener 的 onClick 方法。
推荐阅读: