概述
一圖勝千言:

Activity對觸摸事件的分發(fā)
- 我們首先來看一下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。
- 我們來看一下PhoneWindow是如何傳遞觸摸事件的:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
可以看到事件被傳遞給了Activity的DecorView。我們知道,DecorView是Activity的頂級視圖。他是PhoneWindow的一個內(nèi)部類。
- 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;
}
- mCloseOnTouchOutside是一個boolean變量,它是由Window的android:windowCloseOnTouchOutside屬性值決定。
- isOutOfBounds(context, event)是判斷該event的坐標(biāo)是否在context(對于本文來說就是當(dāng)前的Activity)之外。是的話,返回true;否則,返回false。
- 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