[TOC]
参考:
概览(以ACTION_DOWN为例)
从上到下依次为Activity、ViewGroup、View
- dispatchTouchEvent和onTouchEvent一旦返回true,事件就停止传递了
- dispatchTouchEvent和onTouchEvent返回false,都会回传给父控件的onTouchEvent处理(其中Activity会终止)
但其实调用顺序完整的说法应该是
Activity>PhoneWindow>DecorView>ViewGroup>View
onTouch
dispatchTouchEvent相当于onTouch
这几个方法的优先级是onTouch>onTouchEvent>onClick
onTouch返回true,相当于dispatchTouchEvent返回true
而onClick是在onTouchEvent中调用的
关于ACTION_MOVE和ACTION_UP
- 当dispatchTouchEvent进行事件分发的时候,只有前一个事件(如ACTION_DOWN)返回true,才会收到MOVE和UP的事件。且一个View一旦决定拦截事件,那么这一个事件序列都由它来处理,而不会再调用onInterceptTouchEvent了
- 如果在某个控件的dispatchTouchEvent返回true消费事件,那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP
- 如果我们在onTouchEvent中消费了事件,那么MOVE和UP事件从上往下传到这个View后就不再往下传递了,而直接传给自己的onTouchEvent并结束本次事件传递过程
结合代码分析
dispatchTouchEvent
Activity#dispatchTouchEvent
- Activity调用了window的dispatchTouchEvent
- window调用了mDecor的dispatchTouchEvent
- mDecor调用了super即FrameLayout的dispatchTouchEvent
1 | public boolean dispatchTouchEvent(MotionEvent ev) { |
ViewGroup#dispatchTouchEvent
伪代码实现
1 | public boolean dispatchTouchEvent(MotionEvent ev) { |
真实代码
- 首先判断是否需要调用onInterceptTouchEvent,mFirstTouchTarget在事件不被拦截并且交给子元素处理时不为空,即有子View处理事件时,此值不为空
- disallowIntercept由子View的requestDisallowInterceptTouchEvent决定,但是这个对ACTION_DOWN无效,因为在dispatchTouchEvent开头会重置。
1 | // check for interception |
接下来遍历子View,满足:
- 没有进行动画
- 点击的位置在View的坐标范围内的View会被调用dispatchTouchEvent
View#dispatchTouchEvent
调用步骤:
- onTouch(注意View是disabled的话,只要是clickable或者longclickable仍然可以消费点击事件,只是不会生效)
- onTouchEvent
1 | public boolean dispatchTouchEvent(MotionEvent event) { |