一.前言
-
事件最开始是从屏幕驱动得来的,屏幕驱动调到了Linux层,linux层调到了WindowManagerService,再调到Activity,再调用PhoneWindow;
-
事件分发:在面试方面几乎是必考题,可以说对事件了解多深,对自定义控件开发水平就有多高。事件不处理好,各种时间冲突,卡顿是家常便饭。
-
学习事件分发的目的
- 面试容易考;
- 开发自定义控件容易用到;
- 排除问题比较复杂;
- 熟悉Android事件机制有助于理解Android系统;
-
手机接收产生事件的本质
- 手机屏幕得到用户点击的坐标,经过电容传递给传感器,传感器转换成电频传递给电路板,电路板再传递给Linux层,Linux通过jni接口将坐标写入到WindowManagerService的队列(坐标封装成Event事件),WindowManagerService就会去读取队列中的坐标,然后分发给Activity;
- MotionEvent的产生:通过屏幕驱动,由底层产生的;
- WindowManagerService:是一个窗口管理系统服务,它的主要功能包含如下
- 窗口管理,绘制
- 转场动画--Activity切换动画
- Z-ordered的维护
- Activity窗口显示前后顺序
- 输入法管理
- 系统事件分发线程
- 源码部分(sdk版本为28),我们只需要看Activity相关的即可,jni层没必要去看;
二.Activity的dispatchTouchEvent方法
//通过注释可以了解到
//该方法是被用户触摸屏幕的进程(可以理解成WindowManagerService)去调用
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//调用PhoneWindow的superDispatchTouchEvent方法
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
- WindowManagerService:其是一个独立的进程(管理手机app窗体的),内部有一个main函数,我们需要明白的是WindowManagerService会主动调用Activity的dispatchTouchEvent方法即可;
- 分析PhoneWindow的superDispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用DecorView的superDispatchTouchEvent方法
return mDecor.superDispatchTouchEvent(event);
}
//DecorView类的superDispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用ViewGroup的dispatchTouchEvent方法
return super.dispatchTouchEvent(event);
}
- 到此,我们先停下来,建议大家直接学习Android事件分发机制 详解攻略,您值得拥有,我不太愿意花大量时间重复造轮子。关于这篇文章,是非常的优秀,但是我还是提出一些个人的建议,没有提到递归思想(如果不懂递归,直接看作者画的图,会有点晕),我们一定要懂的什么是递归,实际上我们分析事件分发的源码,实际上只分析Activity的dispatchTouchEvent方法,但是内部涉及到方法的嵌套调用,牵扯的内容就非常多,一些逻辑需要回溯。另外,为什么ViewGroup的拦截方法只会走一次等等;
- 另外还需要学习图解 Android 事件分发机制,这篇文章更优,很有必要学习一下;
- 第三部分,我决定补充文章中未提及到的细节和注意事项;
三.其它
1.什么时候产生cancel事件
- 点击控件的时候,移出到父容器之外了。
3.为什么ViewGroup的拦截方法只会走一次
- ViewGroup的dispatchTouch-Event方法中
//ViewGroup在如下两种情况下会判断是否要拦截当前事件:事件类型为ACTION_DOWN或者mFirstTouchTarget ! = null
//ACTION_DOWN事件好理解,那么mFirstTouchTarget ! = null是什么意思呢?这个从后面的代码逻辑可以看出来,当事件由ViewGroup的子元素成功处理时,mFirstTouchTarget会被赋值并指向子元素,换种方式来说,当ViewGroup不拦截事件并将事件交由子元素处理时mFirstTouchTarget ! = null。反过来,一旦事件由当前ViewGroup拦截时,mFirstTouchTarget ! = null就不成立
//那么当ACTION_MOVE和ACTION_UP事件到来时,由于(actionMasked ==MotionEvent. ACTION_DOWN || mFirstTouchTarget ! = null)这个条件为false,将导致ViewGroup的onInterceptTouchEvent不会再被调用,并且同一序列中的其他事件都会默认交给它处理。
3.其它
- 可以多个view响应down事件;
- onClick会发生的前提是当前View是可点击的,并且它收到了down和up的事件(可自行用手机app测试);
- 那么当ACTION_MOVE和ACTION_UP事件到来时,由于(actionMasked ==MotionEvent. ACTION_DOWN || mFirstTouchTarget ! = null)这个条件为false,将导致ViewGroup的onInterceptTouchEvent不会再被调用,并且同一序列中的其他事件都会默认交给它处理;
- mFirstTouchTarget其实是一种单链表结构。mFirstTouchTarget是否被赋值,将直接影响到ViewGroup对事件的拦截策略;
- 通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外;
评论区