有什么料?
从这篇文章中你能获得这些料:
- 了解一次触摸事件究竟是如何产生的?
- 了解触摸事件究竟是如何传递的?
- 学会从根源处分析你的App中的滑动冲突。
- 能够更自信的创作出具有复杂交互的App。
- 收获一张图,帮助你理解和使用Android的触摸事件分发。
老规矩,先来看图吧。
在你触摸屏幕之后
首先在上图中找到那只黑手,它是一次触摸事件的开始。
当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]
目录下。对,你没看错,事件被搞成文件保存了下来!
接着,系统创建的一个InputReaderThread
线程loop起来让EventHub调用getEvent()
不断的从/dev/input/
文件夹下读取输入事件。
然后InputReader则从EventHub中获得事件交给InputDispatch。
而InputDispatch又会把事件分发到需要的地方,比如ViewRootImpl的WindowInputEventReceiver中。
以上过程是在底层中完成,大部分由c++实现,我们了解流程就行。
下面从ViewRootImpl收到触摸事件开始分析触摸事件的去向。
触摸事件的旅行
第一站:从InputEventReceiver开始
我们现在图中找到WindowInputReceiver。可以看到,它继承了InputEventReceiver,并且是ViewRootImpl的一个内部类。
ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver{
@Override
public void onInputEvent(InputEvent event) {
...
enqueueInputEvent(event, this, 0, true);
...
}
}
当一个输入事件产生时(这里我们认为是触摸事件),会回调InputEventReceiver.onInputEvent()。从名字也可以看出,它是接收输入事件的。然后进一步调用 ViewRootImpl.enqueueInputEvent() 将输入事件加入单链表队列。
ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver,
int flags,
boolean processImmediately) {
...
mPendingInputEventTail = q;
//进行队列操作后,
//将ViewRootImpl的mPendingInputEventTail复制为新的触摸事件。
...
if (processImmediately) {
doProcessInputEvents();
//立即处理事件
} else {
scheduleProcessInputEvents();
//走一遍Handler延迟处理事件
}
}
上面这个方法主要对触摸事件进行队列操作(即排了个序),然后再根据processImmediately
参数,决定是立即处理,还是延后处理。
下面看看是如何进行处理的。
void doProcessInputEvents() {
while (mPendingIn