Android之事件分发基础篇 [ 一 ]
Android 事件分发之源码分析系列
Android 之事件分发基础篇 [ 一 ]
Android 之 ViewGroup 事件分发深入源码分析 [ 二 ]
Android 之 View 事件分发深入源码分析 [ 三 ]
Android 之 View 事件分发深入源码分析 [ 总结 ]
网上看了很多博客, 也看了一些书籍. 琢磨了很久, 始终感觉无从下手. 但是还是要写出来整理一下, 免得以后忘记, 以下整理来自不同的博客还有书籍.
还是先从基础的理论开始.
1.1 事件分发的对象
事件分发的对象就是 MotionEvent 对象
每当用户触摸屏幕时, 就会产生 Touch 事件. 而系统会将 Touch 中包含的触摸位置,时间等一些信息封装成 MotionEvent 对象.
1.2 事件的类型
事件的类型分为以下 4 种.
MotionEvent.ACTION_DOWN 按下View(所有事件的开始)
MotionEvent.ACTION_UP 抬起View(与DOWN对应)
MotionEvent.ACTION_MOVE 滑动View
MotionEvent.ACTION_CANCEL 取消事件(非人为原因)
按下, 滑动, 抬起, 取消这几种事件组成了一个事件流. 事件流以按下为开始, 中间可能有若干次滑动, 以抬起或取消作为结束.
(在 Android 对事件分发的处理过程中, 主要是对按下事件做分发, 进而找到能处理按下事件的 View. 对于后续事件(滑动, 抬起), 则直接分发给能处理按下事件的 View.)
1.3 事件分发的本质
将触摸事件传递到某个具体 View 处理的过程. 也可以理解为 事件的传递过程即为 分发过程.
即当一个 MotionEvent 产生了以后, 系统需要把这个事件传递给一个具体的 View, 而这个过程就是分发过程.
1.4 事件分发的组件
分发事件的组件, 也称为分发事件者, 包括了 Activity, ViewGroup, View. 它们的关系如下图所示,
关系图
组件 | 特点 | 举例 |
---|---|---|
Activity | 安卓视图类 | 如MainActivity |
ViewGroup | View的容器,可以包含若干View | 各种布局类 |
View | UI类组件的基类 | 如按钮、文本框 |
可以看出来. Activity 包含了 ViewGroup, 而 ViewGroup 又可以包含多个 View.
1.5 核心方法
组件 | dispatchTouchEvent | onTouchEvent | onInterceptTouchEvent |
---|---|---|---|
Activity | 存在 | 存在 | 不存在 |
ViewGroup | 存在 | 存在 | 存在 |
View | 存在 | 存在 | 不存在 |
从表格中看,dispatchTouchEvent(),onTouchEvent()方法存在于上文的三个组件中. 而onInterceptTouchEvent()为ViewGroup独有.
( ViewGrou 中, 实际上是没有 onTouchEvent() 方法的. 但是因为 ViewGroup 继承自 View. 而 View 拥有 onTouchEvent() 方法, 所以 ViewGroup 对象也是可以调用 onTouchEvent() 方法的. )
简单的介绍这三个方法, 后续几篇中会从源码一步一步的对这三个方法进行分析.
- public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件分发. 如果事件能够传递给当前 View. 那么此方法一定会被调用.返回结果受当前 View 的 onTouchEvent 和下级 View 的 dispatchTouchEvent 方法的影响. 表示是否消费当前事件.
- 如果某个 View 的该方法返回 True, 则表示当前 View 对这个事件已经进行了处理, 结束分发.并且调用自身的 onTouchEvent() 方法.
- 返回 False, 表示当前 View 不对该事件进行处理, 需要按照规则继续分发事件.
- 在不覆写这个方法的情况下, 除了一些特殊 View, 剩余组件都是默认返回 False 的.
- public boolean onInterceptTouchEvent(MotionEvent ev)
在 dispatchTouchEvent 方法内被调用. 用来判断是否拦截某个事件, 如果当前 ViewGroup 拦截了某个事件, 那么在同一个事件序列中, 此方法不会被再次调用, 返回结果表示是否拦截当前事件.
- 该方法返回默认返回 False.
- 如果自定义的 ViewGroup 希望拦截事件, 不希望事件继续向下分发即不希望事件往子 View 传播, 可以覆写该方法并且返回 True. 即可阻止向下的传播过程.
- 在 ViewGroup 的 dispatchTouchEvent 中, 肯定会调用该方法, 根据方法的返回值来确定如何处理. 若该方法返回 True, 事件将被拦截. 交给自身的 onTouchEvent() 处理. 如果返回 False, 则继续传递给下面的子 View 继续执行分发流程.
- public boolean onTouchEvent(MotionEvent ev)
在 dispatchTouchEvent 方法内被调用. 用来处理事件. 返回结果表示是否消费当前事件. 如果不消费, 则在同一个事件序列中, 当前 View 无法再次接收到事件
- 该方法默认返回 False, 表示没有对事件进行处理即不消费事件.
- 返回 True, 表示已经处理/消费了事件.
- 如果设置了 View 的 onClickListener , 在该方法内会调用 onClick() 方法.
- 如果设置了 View 的 onTouchListener, 重写的 onTouch 方法优先级 高于 onTouchEvent .
这些方法在调用关系中体现了一个类似"递归"的调用过程, 通过 dispatchTouchEvent() 将事件传递下去, 又通过调用 onTouchEvent() 的返回值将事件传递上来. 中间的这一过程可以通过 onInterceptTouchEvent() 方法(针对 ViewGroup) 或者另外负责分发的方法返回 True, 都可以提前终止这一类似"递归"的调用过程. 进而让事件的处理符合我们的预期