一、 事件分發(fā)的對象是誰?
答:事件
當用戶觸摸屏幕時(View或ViewGroup派生的控件),將產(chǎn)生點擊事件(Touch事件)。
Touch事件相關(guān)細節(jié)(發(fā)生觸摸的位置、時間、歷史記錄、手勢動作等)被封裝成MotionEvent對象
主要發(fā)生的Touch事件有如下四種:
- MotionEvent.ACTION_DOWN:按下View(所有事件的開始)(只會產(chǎn)生一次)
- MotionEvent.ACTION_MOVE:滑動View
- MotionEvent.ACTION_CANCEL:非人為原因結(jié)束本次事件
- MotionEvent.ACTION_UP:抬起View(與DOWN對應)(只會產(chǎn)生一次)
事件列:從手指接觸屏幕至手指離開屏幕,這個過程產(chǎn)生的一系列事件 任何事件列都是以DOWN事件開始,UP事件結(jié)束,中間有無數(shù)的MOVE事件,如下圖:

二、事件分發(fā)的本質(zhì)
答:將點擊事件(MotionEvent)向某個View進行傳遞并最終得到處理
即當一個點擊事件發(fā)生后,系統(tǒng)需要將這個事件傳遞給一個具體的View去處理。這個事件傳遞的過程就是分發(fā)過程。
三、事件在哪些對象之間進行傳遞?
答:Activity、ViewGroup、View
一個點擊事件產(chǎn)生后,傳遞順序是:Activity(Window) -> ViewGroup -> View
Android的UI界面是由Activity、ViewGroup、View及其派生類組合而成的

View是所有UI組件的基類
一般Button、ImageView、TextView等控件都是繼承父類View
ViewGroup是容納UI組件的容器,即一組View的集合(包含很多子View和子VewGroup),
其本身也是從View派生的,即ViewGroup是View的子類
是Android所有布局的父類或間接父類:項目用到的布局(LinearLayout、RelativeLayout等),都繼承自ViewGroup,即屬于ViewGroup子類。
與普通View的區(qū)別:ViewGroup實際上也是一個View,只不過比起View,它多了可以包含子View和定義布局參數(shù)的功能。
四、 事件分發(fā)過程由哪些方法協(xié)作完成?
答:dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()

五、 總結(jié)
Android事件分發(fā)機制的本質(zhì)是要解決:
點擊事件由哪個對象發(fā)出,經(jīng)過哪些對象,最終達到哪個對象并最終得到處理。
這里的對象是指Activity、ViewGroup、View
Android中事件分發(fā)順序:Activity(Window) -> ViewGroup -> View
事件分發(fā)過程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三個方法協(xié)助完成
經(jīng)過上述3個問題,相信大家已經(jīng)對Android的事件分發(fā)有了感性的認知,接下來,我將詳細介紹Android事件分發(fā)機制。
六、事件分發(fā)機制方法介紹
Android事件分發(fā)流程如下:(必須熟記)
Android事件分發(fā)順序:Activity(Window) -> ViewGroup -> View

super:調(diào)用父類方法(在這里Activity的父類是ViewGroup,ViewGroup的父類是View)
true:消費事件,即事件不繼續(xù)往下傳遞
false:不消費事件,事件也不繼續(xù)往下傳遞 / 交由給父控件onTouchEvent()處理
看圖總結(jié):首先當你觸摸的屏幕的時候會一口氣產(chǎn)生至少2個事件,一個down,一個up,其余就是你手指移動過程中產(chǎn)生的move事件。首先down事件會先執(zhí)行activity的dispatchTouchEvent方法,不管它返回true或者false都會直接在這里被消費,不會繼續(xù)往下傳遞。只有調(diào)用父類方法的時候會執(zhí)行ViewGroup的dispatchTouchEvent方法,此時假如dispatchTouchEvent返回true也代表在此消費,不會繼續(xù)傳遞,假如返回false,表示不分發(fā),則交還給Activity,說大家都處理不了,你自己處理吧。假如dispatchTouchEvent返回父類,則會調(diào)用ViewGroup的onInterceptTouchEvent,返回true表示攔截這個事件,交給自己的onTouchEvent方法消費事件,假如返回false,表示不攔截,則交給View的dispatchTouchEvent方法,同樣的做處理,之后交給View的onTouchEvent方法,返回true代表消費,返回false,表示無法處理,交還給上級。
其實,這些不需要硬記,實在開發(fā)中遇到不知道哪里返回true,哪里返回false時,回來看看這個順序。最主要的是多去用用就記住了。下面我們看一般的事件傳遞。
七、一般事件傳遞

7.1 默認情況
即不對控件里的方法(dispatchTouchEvent()、onTouchEvent()、onInterceptTouchEvent())進行重寫或更改返回值
那么調(diào)用的是這3個方法的默認實現(xiàn):調(diào)用父類的方法
事件傳遞情況:
從Activity A---->ViewGroup B--->View C,從上往下調(diào)用dispatchTouchEvent()
再由View C--->ViewGroup B --->Activity A,從下往上調(diào)用onTouchEvent()
7.2處理事件
假設(shè)View C希望處理這個點擊事件,即C被設(shè)置成可點擊的(Clickable)或者覆寫了C的onTouchEvent方法返回true。
事件傳遞情況:
DOWN事件被傳遞給C的onTouchEvent方法,該方法返回true,表示處理這個事件
因為C正在處理這個事件,那么DOWN事件將不再往上傳遞給B和A的onTouchEvent();
該事件列的其他事件(Move、Up)也將傳遞給C的onTouchEvent()
7.3 攔截DOWN事件
假設(shè)ViewGroup B希望處理這個點擊事件,即B覆寫了onInterceptTouchEvent()返回true、onTouchEvent()返回true。 事件傳遞情況:
DOWN事件被傳遞給B的onInterceptTouchEvent()方法,該方法返回true,表示攔截這個事件,即自己處理這個事件(不再往下傳遞)
調(diào)用onTouchEvent()處理事件(DOWN事件將不再往上傳遞給A的onTouchEvent())
該事件列的其他事件(Move、Up)將直接傳遞給B的onTouchEvent()
該事件列的其他事件(Move、Up)將不會再傳遞給B的onInterceptTouchEvent方法,該方法一旦返回一次true,就再也不會被調(diào)用了。
7.4 攔截DOWN的后續(xù)事件
假設(shè)ViewGroup B沒有攔截DOWN事件(還是View C來處理DOWN事件),但它攔截了接下來的MOVE事件。
DOWN事件傳遞到C的onTouchEvent方法,返回了true。
在后續(xù)到來的MOVE事件,B的onInterceptTouchEvent方法返回true攔截該MOVE事件,但該事件并沒有傳遞給B;這個MOVE事件將會被系統(tǒng)變成一個CANCEL事件傳遞給C的onTouchEvent方法
后續(xù)又來了一個MOVE事件,該MOVE事件才會直接傳遞給B的onTouchEvent()
后續(xù)事件將直接傳遞給B的onTouchEvent()處理
后續(xù)事件將不會再傳遞給B的onInterceptTouchEvent方法,該方法一旦返回一次true,就再也不會被調(diào)用了。
C再也不會收到該事件列產(chǎn)生的后續(xù)事件。
八、源碼分析
View中dispatchTouchEvent()的源碼分析
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
從上面可以看出:
只有以下三個條件都為真,dispatchTouchEvent()才返回true;否則執(zhí)行onTouchEvent(event)方法
- 第一個條件:mOnTouchListener != null;
- 第二個條件:(mViewFlags & ENABLED_MASK) == ENABLED;
- 第三個條件:mOnTouchListener.onTouch(this, event);
下面,我們來看看下這三個判斷條件:
第一個條件:mOnTouchListener!= null
//mOnTouchListener是在View類下setOnTouchListener方法里賦值的
public void setOnTouchListener(OnTouchListener l) {
//即只要我們給控件注冊了Touch事件,mOnTouchListener就一定被賦值(不為空)
mOnTouchListener = l;
}
第二個條件:(mViewFlags & ENABLED_MASK) == ENABLED
該條件是判斷當前點擊的控件是否enable
由于很多View默認是enable的,因此該條件恒定為true
第三個條件:mOnTouchListener.onTouch(this, event)
回調(diào)控件注冊Touch事件時的onTouch方法
//手動調(diào)用設(shè)置
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
如果在onTouch方法返回true,就會讓上述三個條件全部成立,從而整個方法直接返回true。
如果在onTouch方法里返回false,就會去執(zhí)行onTouchEvent(event)方法。
下面看onTouchEvent(event)**的源碼分析
主要這里面調(diào)用到了performClick()方法,關(guān)注這個方法即可
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
只要mOnClickListener不為null,就會去調(diào)用onClick方法;
那么,mOnClickListener又是在哪里賦值的呢?請繼續(xù)看:
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
當我們通過調(diào)用setOnClickListener方法來給控件注冊一個點擊事件時,就會給mOnClickListener賦值(不為空),即會回調(diào)onClick()。
結(jié)論
onTouch()的執(zhí)行高于onClick()
每當控件被點擊時:如果在回調(diào)onTouch()里返回false,就會讓dispatchTouchEvent方法返回false,那么就會執(zhí)行onTouchEvent();如果回調(diào)了setOnClickListener()來給控件注冊點擊事件的話,最后會在performClick()方法里回調(diào)onClick()。
onTouch()返回false(該事件沒被onTouch()消費掉) = 執(zhí)行onTouchEvent() = 執(zhí)行OnClick()
如果在回調(diào)onTouch()里返回true,就會讓dispatchTouchEvent方法返回true,那么將不會執(zhí)行onTouchEvent(),即onClick()也不會執(zhí)行;
onTouch()返回true(該事件被onTouch()消費掉) = dispatchTouchEvent()返回true(不會再繼續(xù)向下傳遞) = 不會執(zhí)行onTouchEvent() = 不會執(zhí)行OnClick()
onTouch()和onTouchEvent()的區(qū)別
這兩個方法都是在View的dispatchTouchEvent中調(diào)用,但onTouch優(yōu)先于onTouchEvent執(zhí)行。
如果在onTouch方法中返回true將事件消費掉,onTouchEvent()將不會再執(zhí)行。