这篇博客主要介绍在一个TouchEvent从Activity传递到View的流程,照例附上一张流程图:
我们的TouchEvent首先接收到这个TouchEvent的是Acitivty中的dispatchTouchEvent(MotionEvent ev)方法,这个方法里面第19行是将这个Touch事件分发下去。但这只是在这个事件在窗口内,如果在窗口外,
则由Activity本身的onTouchEvent方法去处理这个Touch事件,大概逻辑流程就是如此,下面贴上两个方法的源码和注释:
/**
* 处理Touch屏幕事件的时候被回调。在这些事件分发给window之前你能重载这个方法拦截所有touch屏
* 幕事件,一定要去实现应该正常处理的touch屏幕事件。
* Called to process touch screen events. You can override this to
* intercept all touch screen events before they are dispatched to the
* window. Be sure to call this implementation for touch screen events
* that should be handled normally.
*
* @param ev The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//如果有窗口接收这个事件就走这个流程
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Acitviy中onTouchEvent的源码:
/**
* //当Touch屏幕事件没有被任何在窗口之下的View处理时会调用该方法,这个通常用于处理超出Window
* 边界之外没有View去接收的Touch屏幕事件.
* Called when a touch screen event was not handled by any of the views
* under it. This is most useful to process touch events that happen
* outside of your window bounds, where there is no view to receive it.
*
* @param event The touch screen event being processed.
*
* //如果消耗这个事件返回ture,没有则返回false,这个方法默认返回ture.
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
那么问题来了,Activity如果本身不处理TouchEvent,那么事件分发给谁?
其实Activity的里面是一个Window窗口,该窗口的抽象类是Window,真正的实现该类中的方法是在PhoneWindow当中,Window被作为一个顶级的View添加到WindowManager当中,同样附上源码和注释:
/**
* //为顶层窗口的外观和行为策略设计的基类。这个类的一个实例应该作为一个顶级View添加到
* //WindowManager里面。例如背景,它提供标准的UI策略,标题空间,默认的key处理。
*
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* //当你需要一个Window的实例时,仅仅只有android.view.PhoneWindw实现了这个抽象类。
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window
/**
* //被用于自定义窗口,例如Diaog,通过touch屏幕事件通向View的更下层,
* 应用开发层不需要实现和回调它。
* Used by custom windows, such as Dialog, to pass the touch screen event
* further down the view hierarchy. Application developers should
* not need to implement or call this.
*
*/
public abstract boolean superDispatchTouchEvent(MotionEvent event);
/**
* 安卓指定的窗口
* Android-specific Window.
* <p>
* todo: need to pull the generic functionality out into a base class
* in android.widget.
*
* @hide
*/
public class PhoneWindow extends Window
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
我们可以发现PhoneWindow该窗口依然没有去实现这个方法,而是交给了下面最外层的View(DecorView),而DecorView是继承自FrameLayout,而FrameLayout中并没有重写superDispatchTouchEvent(MotionEvent event)这个方法,最终这个方法的实现是在ViewGroup中的
dispatchTouchEvent(event)方法中,整个Touch事件从Activity传到顶级View的流程便是如此。
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}