对于android的触摸事件,一直以来都有点模糊,所以决定搞搞清楚.这里一共分三部分来解决这个问题:第一部分:触摸事件是如何起源的.第二部分:view是如何处理触摸事件的.第三部分:viewgroup是如何分发和处理触摸事件的.
这一次先看第一部分:触摸事件是如何起源的.
要理解这个问题,首先应该知道下面三点:
- 每一个需要显示到手机上的视图最总都是通过WindowManager.addview()的方式来实现的,比如我们常用的activity/popwindow/状态栏/锁屏/来闹钟界面等!
- 在WindowManager.addview()过程中实际上是调用WindowManagerGlobal.addView(),在这个方法里面是通过创建一个ViewRootImpl的对象,最后将需要显示的视图view加入到这个ViewRootImpl.也就是说显示的视图起源于ViewRootImpl.
- 一个activity的视图起源于一个DecorView:DecorView其实是一个FrameLayout,我们在activity中通过setContentView()添加的显示view最终都是会加入这个DecorView里面的.
下面来看看触摸事件流程,看看触摸事件是如何流入视图的view的:
(1)每一个显示的窗口都是需要通过WindowManager.addview()来添加进去的,这个过程中会为这个显示的view创建一个该view与系统交互的ViewRootImpl.这里的触摸事件就是开始从ViewRootImpl来的.
(2)ViewRootImpl中定义了一个WindowInputEventReceiver来接收触摸事件.然后把触摸事件传递给mView.在activity中mView就是DecorView
(3)通过ViewRootImpl最后调用了view的dispatchPointerEvent(MotionEvent event).这样触摸事件就传递给了显示的view了
我们知道,对于activity来说,ViewRootImpl首先时把触摸事件传递给了DecorView的dispatchPointerEvent().下面来看看DecorView的dispatchPointerEvent源码:
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)//会首先调用Callback的dispatchTouchEvent方法.
: super.dispatchTouchEvent(ev);
}
这里要知道这个Callback对象那里来的.来看看getCallback()的源码,在Window里面定义如下:
public final Callback getCallback() {
return mCallback;//看到了吧,其实很简单,直接返回mCallback即可.
}
那么mCallback是在何时赋值的呢?我们知道,创建一个activity就会为这个activity创建他的window,在activity中你会发现:原来activity已经implements了这个Callback
通过activity的代码你会发现其实在创建window的时候就给这个window的mCallback赋值了:
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);//这里的this就是当前的activity
所以最终DecorView调用 dispatchTouchEvent会调用到activity的dispatchTouchEvent()方法.
(4)来看看activity的dispatchTouchEvent做来些什么:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {//会调用Window的superDispatchTouchEvent方法,如果返回true,则结束,否则调用这个activity的onTouchEvent方法
return true;
}
return onTouchEvent(ev);
}
(5)所以来看看 Window的superDispatchTouchEvent方法.Window是一个虚类,其实现类是PhoneWindow
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);//实际上是调用DecorView的superDispatchTouchEvent方法
}
(6)来看看DecorView的superDispatchTouchEvent的
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);//看到了吧,最终还是调用了DecorView的dispatchTouchEvent方法
}
(7)综上所述:触摸事件从 ViewRootImpl开传递出来,最终会调用显示视图view的最父(最后面的/最老的)view的dispatchTouchEvent.如果返回false,就会调用activity的onTouchEvent()方法.
从上面的分析可以看出,触摸事件都是从dispatchTouchEvent开始的,所以要理解触摸事件的流程其实就要从view的dispatchTouchEvent开始.