电视遥控器,走key事件:
dispatchKeyEventPreIme、dispatchKeyEvent、onKeyDown、view.onKeyListener、view.onClickListener
> 对于key事件来说只有两个action:KeyEvent.ACTION_DOWN和KeyEvent.ACTION_UP
> Activity的dispatchKeyEvent 作为一切事件的入口。
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
return true;
}
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
}
上面是Activity中的源码。上面的代码说如果window自己消费了事件,那么return true。通常我们也不能把系统的window类改成这样。
那么就会将事件分发给decorView。(activity的decorView下的android.id.content就是咱们自己的layout的上层,是一个FrameLayout。)
> 在Activity的dispatchKeyEvent 消费某一事件(即return true)后,那onKeyDown则接收不到该事件
不消费则 return super.dispatchKeyEvent(event); (通常重写事件时,会有默认的返回方式,
一般直接写最后,需要特殊处理并消费的地方才return true)
> 在View中:onKeyPreIme、onKeyDown、onKeyLongPress、onKeyUp、onKeyMultiple、onKeyShortcut(不清楚作嘛的)
还有dispatchKeyEventPreIme、dispatchKeyEvent
> 在ViewGroup中:dispatchKeyEventPreIme、dispatchKeyEvent
> dispatchKeyEventPreIme(在键盘Ime接收前执行) 会比 dispatchKeyEvent先执行,
一般如果没有键盘输入的界面,重写任意一个都可以
> onClick:在一个完整的action_down和action_up周期后,才会执行onClick
> onKeyDown、onKeyListener 基本是一样的,只是看使用时候的范围,是要监听Activity,
还是某一Layout,还是某一个View
总结:dispatch的会先执行,再到onKey;从Activity>ViewGroup(各式Layout)>view; 从action_down>action_up。
一个action_down,从activity的dispatch 往下分发,到viewGroup的dispatch。
dispatch中return true,事件不再向后传
手机,走touch事件:
跟key类似,也是从Activity的dispatchTouchEvent开始。
每个组件含有的相关方法:
Activity : dispatchTouchEvent、onTouchEvent
ViewGroup : dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
View : dispatchTouchEvent、onTouchEvent
示例1:
> 下面的log,含有move动作,都返回false时(注意返回false,最好调用super.对应方法,否则可能事件不会传递):
stone-activity--dispatchTouchEvent--down--false
stone-viewGroup--dispatchTouchEvent--down--false
stone-viewGroup--onInterceptTouchEvent--down--false
stone-view--dispatchTouchEvent--down--false
stone-view--onTouchEvent--down--false
stone-viewGroup--onTouchEvent--down--false
stone-activity--onTouchEvent--down--false
stone-activity--dispatchTouchEvent--move--false
stone-activity--onTouchEvent--move--false
stone-activity--dispatchTouchEvent--up--false
stone-activity--onTouchEvent--up--false
down 事件:
自顶向下:activity(dispatch) -> viewGroup(dispatch, onIntercept) -> View (dispatch);
自底向上:view (onTouch) -> viewGroup(onTouch) -> activity(onTouch)
viewGroup 和 view 没有对 down 事件 进行消费(即返回 true)。就收不到后续的 move、up 事件。只有 activity 来处理 move、up 事件。
示例2:
> 下面的log, 在activity--dispatchTouchEvent—down, 返回true时,
stone-activity--dispatchTouchEvent--down--true
stone-activity--dispatchTouchEvent--move--false
stone-activity--onTouchEvent--move--false
stone-activity--dispatchTouchEvent--up--false
stone-activity--onTouchEvent--up--false
activity 的 dispatch 中的 down 事件被消费,造成了后续链的断开。
示例3:
> 下面的log,在viewGroup—dispatchTouchEvent—down,返回true时,
stone-activity--dispatchTouchEvent--down--false
stone-viewGroup--dispatchTouchEvent--down--true
stone-activity--dispatchTouchEvent--move--false
stone-viewGroup--dispatchTouchEvent--move--false
stone-viewGroup--onTouchEvent--move--false
stone-activity--onTouchEvent--move--false
stone-activity--dispatchTouchEvent--up--false
stone-viewGroup--dispatchTouchEvent--up--false
stone-viewGroup--onTouchEvent--up--false
stone-activity--onTouchEvent--up--false
与示例1相比,在viewGroup—dispatchTouchEvent—down 被消费了,那其后的 down 事件就不传递了。
示例4:
> 下面的log,在viewGroup--onInterceptTouchEvent—down,返回true时,
stone-activity--dispatchTouchEvent--down--false
stone-viewGroup--dispatchTouchEvent--down--false
stone-viewGroup--onInterceptTouchEvent--down--true
stone-viewGroup--onTouchEvent--down--false
stone-activity--onTouchEvent--down--false
stone-activity--dispatchTouchEvent--move--false
stone-activity--onTouchEvent--move--false
stone-activity--onTouchEvent--move--false
stone-activity--dispatchTouchEvent--up--false
stone-activity--onTouchEvent--up--false
拦截后,直接执行 viewGroup 的 onTouchEvent(down)
> 下面的log,在view--dispatchTouchEvent—down,返回true时,
stone-activity--dispatchTouchEvent--down--false
stone-viewGroup--dispatchTouchEvent--down--false
stone-viewGroup--onInterceptTouchEvent--down--false
stone-view--dispatchTouchEvent--down--true
stone-activity--dispatchTouchEvent--move--false
stone-viewGroup--dispatchTouchEvent--move--false
stone-viewGroup--onInterceptTouchEvent--move--false
stone-view--dispatchTouchEvent--move--false
stone-view--onTouchEvent--move--false
stone-activity--dispatchTouchEvent--up--false
tone-viewGroup--dispatchTouchEvent--up--false
stone-viewGroup--onInterceptTouchEvent--up--false
stone-view--dispatchTouchEvent--up--false
stone-view--onTouchEvent--up--false
stone-activity--onTouchEvent--up--false
结合上面的示例log,总结:
> 组件传递顺序:Activity、ViewGroup、View
> 事件传递顺序:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
> 默认传递顺序:
dispatchTouchEvent-down从上到下(如果经过ViewGroup,还会经过onInterceptTouchEvent-down);
直至传递到View级的onTouchEvent,onTouchEvent 再从下到上传递,即View、ViewGroup、Activity;
若down事件没有任何地方拦截,后续的move、up只发生在Activity
> 在Activity、ViewGroup、View的 dispatchTouchEvent-down 时,返回true:表示消费掉该down事件,down事件不会再传递;
且后续的move、up事件,会经过当前组件的dispatchTouchEvent和onTouchEvent
> 再说一些:
a. dispatchTouchEvent 某一级组件的action事件,返回true,就会消费掉该action事件,不继续传递它,
且后续action会经过当前组件的dispatchTouchEvent和onTouchEvent
b. 如果想执行到ViewGroup或View的onTouchEvent-action(move|up),
那么要在该组件的dispatchTouchEvent-down或onTouchEvent-down时,需要返回true
c. ViewGroup的onInterceptTouchEvent,若某个action事件返回true,则也不会向子view传递,
但会转发事件到当前ViewGroup的onTouchEvent
注:
> View#onTouchEvent 默认会返回 true,即消费事件;除非它是不可点击的(clickable 和 longClickable 同为 false)
View的 dispatchTouchEvent 中,会判断是否有 OnTouchListener,有就先执行,且OnTouchListener消费了事件,则 onTouchEvent就不再执行。
在 up 后,还会判断是否有OnClickListener,若有则执行。可见它们的优先级:OnTouchListener > onTouchEvent > OnClickListener。
> 还有个特殊方法:ViewGroup#requestDisallowInterceptTouchEvent(boolean disallowIntercept),true 时用于请求父级容器,不拦截除了 down 事件之外的事件;false 继续父级拦截
ViewGroup 的 down 事件,会重置该方法对应的标记值。
> View#onTouchEvent中,在执行 down、move、up 事件之前,会先判断是否有 TouchDelegate,若执行TouchDelegate#onTouchEvent();
且当该方法返回 true,则View#onTouchEvent不再执行。
> TouchDelegate的构造方法只有一个:public TouchDelegate(Rect bounds, View delegateView);
bounds:包含delegateView在内的 Rect;
delegateView:负责touch 事件的委托 view;
即 当touch事件在bounds范围内,则委托给delegateView来处理
View#setTouchDelegate(TouchDelegate delegate); 用于设置
鼠标,走hover事件:
在Activity中,并没有 dispatchHoverEvent和onHoverEvent, 其它与touch类似
在ViewGroup中多一个 onInterceptHoverEvent 鼠标有三种动作
> ACTION_HOVER_ENTER 指针悬浮在view上
> ACTION_HOVER_MOVE 指针在view上移动
> ACTION_HOVER_EXIT 指针离开view边界
鼠标目前没调试过,分发原理基本类似touch