- Touch事件如何从屏幕到我们的App
最开始是硬件驱动获取到设备,当系统启动时,在SystemServer进程会启动一系列系统服务,如AMS,WMS等。
其中还有一个就是我们管理事件输入的InputManagerService。这个服务就是用来负责与硬件通信,接受屏幕输入事件。
最终在SystemServer进程中,WindowManagerService根据当前的Window创建了SocketPair用于跨进程通信,App进程的主线程就会监听socket客户端,当收到消息(输入事件)后,回调NativeInputEventReceiver.handleEvent()方法,最终会走到InputEventReceiver.dispachInputEvent方法。经过以上操作App终于拿到输入事件了
2.事件在页面上的分发
Android的事件分发机制相关的类:
public boolean dispatchTouchEvent(event):用于进行点击事件的分发
public boolean onInterceptTouchEvent(event):用于进行点击事件的拦截
public boolean onTouchEvent(event):用于处理点击事件
三个函数的参数均为even,即上面所说的3种类型的输入事件,返回值均为boolean 类型
上面的三种方法的调用关系大致可以用下面的伪代码来描述
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;//事件是否被消费
if (onInterceptTouchEvent(ev)){//调用onInterceptTouchEvent判断是否拦截事件
consume = onTouchEvent(ev);//如果拦截则调用自身的onTouchEvent方法
}else{
consume = child.dispatchTouchEvent(ev);//不拦截调用子View的dispatchTouchEvent方法
}
return consume;//返回值表示事件是否被消费,true事件终止,false调用父View的onTouchEvent方法
}
下面详细的说说这3个方法:
dispatchTouchEvent()是处理触摸事件分发,事件(多数情况)是从Activity的dispatchTouchEvent()开发的;执行getWindow().superDispatchTouchEvent(ev)事件向下分发;
Activity
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//调用Window下的superDispatchTouchEvent()方法
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用Window窗口下的DecorView的superDispatchTouchEvent()方法
return mDecor.superDispatchTouchEvent(event);
}
DecorView
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用DecorView父视图的super.dispatchTouchEvent()方法
return super.dispatchTouchEvent(event);
}
ViewGroup
public boolean dispatchTouchEvent(MotionEvent event) {
...
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
...
return handled;
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
...
//事件向子视图传递
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
...
return handled;
}
onInterceptTouchEvent()是ViewGroup提供的方法,默认返回false,返回true表示拦截;
onTouchEvent()是View中提供的方法,ViewGroup也有这个方法,view中不提供onInterceptTouchEvent();view中默认返回true,表示消费了这个事件;
View里有两个事件回调函数
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
ViewGroup里,有三个回调函数
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
Activity里有两个回调函数
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
事件的分发流程:
3.事件传递流程分析
Android中默认情况下事件传递是由最终的View的接收到,传递过程是从父视图到子视图,也就是从Activity到ViewGroup到View的过程,默认情况下,ViewGroup起到透传的作用;Android中事件传递过程(按箭头方向)处理,如下图
触摸事件是一连串ACTION_DOWN,ACTION_MOVE…MOVE,最后ACTION_UP,触摸事件还有ACTION_CANCEL事件,事件都是从ACTION_DOWN开始的,Activity的dispatchTouchEvent()首先接收到ACTION_DOWN,执行super.dispatchTouchEvent()或者child.dispatchTouchEvent(event),事件向下分发;
dispatchTouchEvent()返回true,后续事件(ACTION_MOVE,ACTION_UP)会在传递,如果返回false,dispatchTouchEvent()就接收不到ACTION_UP,ACTION_MOVE事件;
事件传递顺序如下:
拦截情况我们可以概括为已下几种可能:
1.ACTION_DOWN都没被消费
2.ACTION_DOWM在后面被消费
3.ACTION_DOWN被消费后,后续的UP和MOVE 不被拦截
4.ACTION_DOWN被消费后,后续的UP和MOVE 被拦截的情况
5.ACTION_DOWN一开始就被拦截
下面对 以上不同的情况详细说明:
1.ACTION_DOWN都没被消费
2.ACTION_DOWN被View消费了
3.后续ACTION_MOVE和UP在不被拦截的情况下都会去找VIEW
4.后续的被拦截了
5.ACTION_DOWN一开始就被拦截
Android中的Touch事件都是从ACTION_DOWN开始的:
单手指操作:ACTION_DOWN—ACTION_MOVE----ACTION_UP
多手指操作:ACTION_DOWN—ACTION_POINTER_DOWN—ACTION_MOVE–ACTION_POINTER_UP—ACTION_UP
4.冲突事件处理
1.根据父布局中的onInterceptToucherEven(),判断子布局 如果在滑动,就不做拦截
2.在子布局中 的 dispatchTouchEvent中 判断自己滑动,如果有滑动 请求父布局不要拦截自己 getparent.requestDisallowInterceptToucherEvent(true||false);