Android事件分发机制完全解析(四) : Activity的事件分发机制

很多讲解Android事件分发的博客都提到了ViewViewGroup的事件分发,其实Android事件分发除了上述2个,还有一个就是Activity。

而Android事件分发的起点,就是从Activity开始的。在实际研发工作中,虽然我们用得更多的是ViewGroupView的事件分发机制的知识,但从知识体系的完整性讲,Activity的事件分发机制地位同样重要。

说明: 本文在无特别说明下,源码对应的api版本均为: 28.0.0

老规矩,在正式学习Activity的事件分发机制之前,我们给出结论性的东西,先有一个整体的脉络。带着问题看源码,不容易陷入死胡同之中:
Android事件分发机制完全解析(四) : Activity的事件分发机制_第1张图片Android事件分发机制完全解析(四) : Activity的事件分发机制_第2张图片

dispatchTouchEvent()

代码片1
/**
     * 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) {
       //  ----->分析1
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
          //  ---->分析2
            onUserInteraction();
        }

       //   ------>分析3
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
       
       // ---->分析4
        return onTouchEvent(ev);
    }

上述代码的逻辑比较简单: 首先判断MotionEvent是不是ACTION_DOWN,如果是的话,执行onUserInteraction()方法,然后判断getWindow().superDispatchTouchEvent(ev)是否为true,如果为true,就不会执行onTouchEvent()方法,如果为false则执行onTouchEvent()方法。

1.分析1和2

一般事件列开始都是DOWN事件 = 按下事件,故代码片1第10行代码一般为true.进入到第17行代码中。

我们来看一下第17行代码(即onUserInteraction(),分析2)的源码:

代码片2
/**
     * Called whenever a key, touch, or trackball event is dispatched to the
     * activity.  Implement this method if you wish to know that the user has
     * interacted with the device in some way while your activity is running.
     * This callback and {@link #onUserLeaveHint} are intended to help
     * activities manage status bar notifications intelligently; specifically,
     * for helping activities determine the proper time to cancel a notfication.
     *
     * 

All calls to your activity's {@link #onUserLeaveHint} callback will * be accompanied by calls to {@link #onUserInteraction}. This * ensures that your activity will be told of relevant user activity such * as pulling down the notification pane and touching an item there. * *

Note that this callback will be invoked for the touch down action * that begins a touch gesture, but may not be invoked for the touch-moved * and touch-up actions that follow. * * @see #onUserLeaveHint() */ public void onUserInteraction() { }

这是一个空方法,什么都没有。还好有注释,不然都不知道这个方法是用来干嘛的了。当此activity在栈顶时,触屏点击按homebackmenu键等都会触发此方法。下拉statubar、旋转屏幕、锁屏不会触发此方法。所以它会用在屏保应用上,因为当你触屏机器 就会立马触发一个事件,而这个事件又不太明确是什么,正好屏保满足此需求。

2.分析3

我们来看 getWindow().superDispatchTouchEvent(ev)

由代码片1,首先事件开始交给Activity所属的Window进行分发,如果返回true,整个事件循环结束,返回false意味着事件没人处理,所有View的onTouchEvent都返回了false,那么Activity的onTouchEvent就会被调用。

我们先看superDispatchTouchEvent()

代码片3
   /**
     * 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);

意思大概是说,这个方法会在Dialog等界面中使用到,开发者不需要实现或者调用它。

我们注意到superDispatchTouchEvent()方法是getWindow()调用的,getWindow()方法返回的是一个Window对象。源码中,对Window类的描述是这样的:

代码片4
/**
 * 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.
 *
 * 

The only existing implementation of this abstract class is * android.view.PhoneWindow, which you should instantiate when needing a * Window. */

上述代码(注释)的大概意思是: Window类可以控制顶级View的外观和行为策略,它的唯一实现位于android.view.PhoneWindow中,当你要实例化这个Window类的时候,你并不知道它的细节,因为这个类会被重构,只有一个工厂方法可以使用。

既然Window的唯一实现类是PhoneWindow,那我们直接看PhoneWindow是如何处理点击事件的:

代码片5
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

到这里,逻辑就很清晰了,PhoneWindow将事件直接传递到了mDecor对象。那么mDecor对象又是什么?
跳转到mDecor的声明,代码如下:

代码片6
// This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;

再跳转到DecorView类的定义处,发现这么一行代码:

代码片7
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker......

现在我们清楚了,DecorView继承自FrameLayout,是我们编写所有的界面代码的父类。

然后我们看看mDecor.superDispatchTouchEvent()这个方法干了什么,也就是在DecorView类中,superDispatchTouchEvent()方法的内容:

代码片8
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

上述代码调用的又是super.dispatchTouchEvent,也就是DecorView父类的dispatchTouchEvent方法。

而我们知道, DecorView是继承自FrameLayoutFrameLayout的父类就应该是ViewGroup了,换句话说,最终 getWindow().superDispatchTouchEvent(ev) 其实就是 ViewGroupdispatchTouchEvent()方法了。

总结一下: getWindow().superDispatchTouchEvent(ev)的值取决于,如果视图顶层的ViewGroup-DecorView类-的dispatchTouchEvnent()方法。

  1. 如果返回true,进入if体中, 整个dispatchTouchEvent返回true,即拦截事件,代码片1的第26行代码onTouchEvent()方法也不会执行了。

  2. 如果返回false,则执行代码片1的第26行代码onTouchEvent()方法。

关于ViewGroup的事件分发机制,请看这篇:Android事件分发机制完全解析(三) :ViewGroup的事件分发机制

3.分析4

Activity.onTouchEvent()

代码片9
/**
     * 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.
     *
     * @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;
    }

代码逻辑很简单,第13行代码又是调用Window的方法。
我们在Window.java中搜索shouldCloseOnTouch()方法,代码如下

代码片10
/** @hide */
    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
    
        final boolean isOutside =
                event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
                || event.getAction() == MotionEvent.ACTION_OUTSIDE;
        if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
            return true;
        }
        return false;
    }

mCloseOnTouchOutside是一个boolean变量,它是由Windowandroid:windowCloseOnTouchOutside属性值决定。

isOutOfBounds(context, event)是判断该event的坐标是否在context(对于本文来说就是当前的Activity)之外。是的话,返回true;否则,返回false。

peekDecorView()则是返回PhoneWindow的mDecor。

也就是说,如果设置了android:windowCloseOnTouchOutside属性为true,并且当前事件是ACTION_DOWN,而且点击发生在Activity之外,同时Activity还包含视图的话,则返回true;表示该点击事件会导致Activity的结束。

比较典型的情况就是dialog形的Activity。

也就是说: Activity.onTouchEvent() 正常情况下都是返回false的。如果返回true,一方面拦截了事件,另外一方面finish当前Activity界面。

到此,Activity的事件分发机制全部分析完毕。现在通过一个流程图完成全文:
Android事件分发机制完全解析(四) : Activity的事件分发机制_第3张图片

你可能感兴趣的:(Android知识)