前段时间看了《Android开发艺术探索》,关于 Android 事件分发,写了一些笔记。
事件分发机制的伪代码。
1 | public boolean dispatchTouchEvent(MotionEvent event){ |
对于一个根 ViewGroup
来说,点击事件产生后,首先会传递给它,它的 dispatchTouchEvent
方法会被调用,如果这个 ViewGroup
的 onInterceptTouchEvent
方法返回 true
则表示它要拦截此事件,事件就会交给它自己处理,即调用它的 onTouchEvent
方法;如果它的 onInterceptTouchEvent
方法返回 false
表示不会拦截这个事件,当前事件就会传递给它的子元素,子元素的 dispatchTouchEvent
方法会被调用,如此循环知道事件被处理。
当一个 View
需要处理事件时,如果它已经设置了 OnTouchListener
,那么 OnTouchListener
中的 onTouch
方法会被回调,这是事件如何处理还要看 onTouch
的返回值,如果返回 true
则当前 View
的 onTouchEvent
方法会被调用;如果返回 false
,那么 onTouchEvent
方法不会被调用。由此可见方法的优先级 onTouch
> onTouchEvent
> onClick
。
点击事件的传递过程遵循以下顺序:Activity
-> Window
-> View
,即事件总是先传递给 Activity
,Activity
再传递给 Window
,最后再传递给顶级 View
,顶级 View
接收到事件后,就会按照事件分发机制分发事件。
如果一个 View
的 OnTouchEvent
方法返回 false
,那么他的父容器的 OnTouchEvent
会被调用,以此类推。如果所有的元素都不处理这个事件,那么这个事件最终会传递给 Activity
处理。
注意:
- 同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中产生的一系列事件,这个事件序列以
down
事件开始,中间含有数量不定的move
事件,最终以up
事件结束。 - 正常情况下,一个事件序列只能被一个
View
拦截并消耗。因为一旦一个元素拦截了某事件,那么同一个序列内的所有事件都会直接交给他处理,因此同一个序列中的事件不能分别有两个View
同时处理。但是通过特殊手段可以做到,比如一个View
将本该有它处理的事件通过OnTouchEvent
强行传递给其他View
处理。 - 某个
View
一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话),并且它的onInterceptTouchEvent
方法不会再被调用。 - 某个
View
一旦开始处理事件,如果它不消耗ACTION_DOWN
事件,那么同一个事件序列里的其他事件都不会再交给他处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent
方法会被调用。 - 如果
View
不消除掉ACTION_DOWN
之外的其他事件,那么这个点击事件就会消失,此时父元素的onTouchEvent
并不会被调用,并且当前View
可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity
处理。 ViewGroup
方法默认不拦截任何事件。Android
源码中ViewGroup
的onInterceptTouchEvent
方法默认返回false
。View
没有onInterceptTouchEvent
方法,只有有子元素的元素才有。所以只要有点击事件传递给View
,就会调用它的onTouchEvent
方法。View
的onTouchEvent
默认都会消耗事件,除非它是不可点击的。View
的longClickable
属性默认都为false
。View
的enable
属性不影响onTouchEvent
的默认返回值。哪怕一个View
是disable
状态的,只要它的clickable
和longClickable
有一个为true
,那么它的onTouchEvent
就返回true
。onClick
发生的前提是当前View
是可点击的。并且它收到了down
和up
的事件。- 事件传递过程是由外到内的,即事件总是先传递给父元素,然后再由父元素下发到子元素。