觸摸事件的分發(fā) (Activity篇)

概述

一圖勝千言:

TouchEvent事件傳遞.png

Activity對觸摸事件的分發(fā)

  1. 我們首先來看一下Activity是如何分發(fā)觸摸事件的:
 public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//將事件傳遞給和該Activity組合的Window對象。
            return true;
        }
        return onTouchEvent(ev);
    }

可以看到,事件被傳遞給了和Activity對應(yīng)的Window對象。通過查看Window源碼我們知道,Window是一個虛擬類。Window的累注釋中明確說明目前只有一個實(shí)現(xiàn)類叫做PhoneWindow。

  1. 我們來看一下PhoneWindow是如何傳遞觸摸事件的:
@Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

可以看到事件被傳遞給了Activity的DecorView。我們知道,DecorView是Activity的頂級視圖。他是PhoneWindow的一個內(nèi)部類。

  1. DecorView對事件的傳遞。
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

        public boolean superDispatchTouchEvent(MotionEvent event) {
            return super.dispatchTouchEvent(event);
        }
}

我們看到,DecorView的superDispatchTouchEvent方法調(diào)用了父類的dispatchTouchEvent(event)方法。而DecorView是繼承自FrameLayout的。FrameLayout繼承自ViewGroup并且沒有重寫dispatchTouchEvent(event)方法。

至此,觸摸事件已經(jīng)從Activity傳遞到了和該Activity對應(yīng)的ViewTree的頂級ViewGroup中。
事件在ViewGroup中的分發(fā)過程,我們將在后續(xù)文章中詳細(xì)分析。這里重點(diǎn)需要了解:Activity在通過dispatchTouchEvent()傳遞觸摸事件的時候,會調(diào)用到ViewGroup的dispatchTouchEvent()。從而實(shí)現(xiàn),將Activity中的觸摸事件傳遞給它所包含的View或ViewGroup。

Activity對觸摸事件的處理

回顧一下Activity的dispatchTouchEvent()方法的源碼:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//將事件傳遞給和該Activity組合的Window對象。
            return true;
        }
        // 如果superDispatchTouchEvent()返回false。
        // 即Activity的根視圖以及根視圖的子視圖都沒有處理該事件的話,則調(diào)用Activity的onTouchEvent()
        return onTouchEvent(ev);
    }

上面小節(jié)描述的情況是getWindow().superDispatchTouchEvent(ev)中一級一級向下傳遞的情況。那么如果getWindow().superDispatchTouchEvent(ev)調(diào)用完成返回false,即即Activity的根視圖以及根視圖的子視圖都沒有處理該事件的話,則調(diào)用Activity的onTouchEvent(),由Activity中的onTouchEvent做出最后的處理。
我們看一下Activity的onTouchEvent()的代碼:

    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        
        return false;
    }

可以看到,Activity中默認(rèn)的onTouchEvent方法很簡單:
只處理一種情況:當(dāng)mWindow.shouldCloseOnTouch(this, event)返回true時調(diào)用finish()方法結(jié)束Activity。其他情況一律不處理。那么mWindow.shouldCloseOnTouch(this, event)在哪種情況下返回true就顯得非常重要。我們來看下Window類中shouldCloseOnTouch()方法的的源碼:

    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
      if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
              && isOutOfBounds(context, event) && peekDecorView() != null) {
          return true;
      }
      return false;
  }
  1. mCloseOnTouchOutside是一個boolean變量,它是由Window的android:windowCloseOnTouchOutside屬性值決定。
  2. isOutOfBounds(context, event)是判斷該event的坐標(biāo)是否在context(對于本文來說就是當(dāng)前的Activity)之外。是的話,返回true;否則,返回false。
  3. peekDecorView()則是返回PhoneWindow的mDecor。
    也就是說,如果設(shè)置了android:windowCloseOnTouchOutside屬性為true,并且當(dāng)前事件是ACTION_DOWN,而且點(diǎn)擊發(fā)生在Activity之外,同時Activity還包含視圖的話,則返回true;表示該點(diǎn)擊事件會導(dǎo)致Activity的結(jié)束。

比較典型的情況就是dialog形的Activity。

下一篇我們將分析事件在ViewTree中的傳遞過程。

參考文獻(xiàn)

http://wangkuiwu.github.io/2015/01/02/TouchEvent-Activity/
http://blog.csdn.net/yanbober/article/details/45932123
http://blog.csdn.net/ns_code/article/details/49848801

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容