参考PPT
安卓系统点击事件处理框架
- 用户的点击事件均被包装为MotionEvent
- MotionEvent描述了用户的行为
- ACTION_DOWN
- ACTION_UP
- ACTION_MOVE
- ACTION_POINTER_DOWN
- ACTION_POINTER_UP
- ACTION_CANCEL
- 使用
MotionEventCompat.getActionMasked(ev)
获取MotionEvent
对应的action
- MotionEvent还包括以下信息
- 点击的位置(x, y坐标)
- 触点的数量(手指)
- 事件发生的时间戳
- 任何一个手势,都是以ACTION_DOWN起始,ACTION_UP结束
- 事件从Activity的dispatchTouchEvent()函数开始,沿着View层次树依次向下传递
- 父元素把事件dispatch到子元素
- 事件能在任意阶段被intercept
- 事件会沿着View层次树依次向下传递,然后又反向向上传递,直到被“消费”
- View如果对手势感兴趣,就必须消费掉ACTION_DOWN的事件
- 出于性能的考虑,同一手势的后续事件将不会按照完整路径进行传递,而是直接传递到消费了ACTION_DOWN事件的View
- 如果所有的View(ViewGroup)都没有消费掉事件,那它将传递到Activity的onTouchEvent()函数中,并结束传递过程,即如果没有被消费,也不会再继续传递了
- 可选的OnTouchListener能在任一View(ViewGroup)上intercept事件,事件被intercept之后,后面的调用将被传入ACTION_CANCEL?啥意思??
Activity.dispatchTouchEvent()
- 总是首先被调用
- Sends event to root view attached to Window
- 如果所有的View(ViewGroup)都没有消费该事件,那么
Activity.onTouchEvent()
将被调用,而且这个函数是最后一个被调用的函数
ViewGroup.dispatchTouchEvent()
- 首先调用
onInterceptTouchEvent()
函数,判断是否需要拦截- 检查是否应该替代子view的处理
- Passes ACTION_CANCEL to active child
- 如果要消费掉同一手势的所有后续事件,需要返回true
- 对所有的孩子,以添加顺序的逆序进行遍历
- 如果点击在孩子的边界内,则调用
child.dispatchTouchEvent()
- 如果没有被当前的孩子消费,则传递到下一个孩子
- 如果点击在孩子的边界内,则调用
- 如果所有的孩子都未消费该事件,则传递给listener,
OnTouchListener.onTouch()
- 如果没有listener,或者listener也未消费,则自己处理,调用
ViewGroup.onTouchEvent()
- Intercepted events jump over child step
- 首先调用
View.dispatchTouchEvent()
- 如果被设置了OnTouchListener,那么将先把事件发送到listener,调用
View.OnTouchListener.onTouch()
- 如果listener没有消费事件,将调用
View.onTouchEvent()
,即自己处理点击事件
- 如果被设置了OnTouchListener,那么将先把事件发送到listener,调用
- 例子
- View对事件不感兴趣
- View对事件感兴趣
- 事件被ViewGroup intercept
- View对事件不感兴趣
- 小结
- 手势以ACTION_DOWN起始,以ACTION_UP结束
- ACTION_DOWN,在每一层View上都会调用
dispatchTouchEvent()
,该View会判断是否对接下来的手势感兴趣,后续的点击事件将直接传递到感兴趣的View - ViewGroup可以intercept一个手势,因为
onInterceptTouchEvent()
是在dispatchTouchEvent()
函数中最先被调用的,如果onInterceptTouchEvent()
返回true,它的孩子将不会收到该手势的后续事件