前言
-
聊之前先簡單看下Activity,PhoneWindow,DecorView的關(guān)系,這樣才更好理解事件的傳遞
aa.png
圖片1.png
捋一下:
在Activity創(chuàng)建完成的時候,會調(diào)用Activity的attach方法來創(chuàng)建一個PhoneWindow對象設(shè)置給Activity,而PhoneWindow對象內(nèi)部又創(chuàng)建一個DecorView柱告,DecorView繼承自FrameLayout,所以說我們通過setContentView設(shè)置的布局文件其實是被添加到DecorView中成為DecorView的一個子View,也就是說最外層的ViewGroup就是DecorView,DecorView這個類是在PhoneWindow中的內(nèi)部類!
可以看下上面圖!加深理解!
感興趣的可以去看下源碼,此處就不詳細的說了,只是簡單的介紹一下!
OK現(xiàn)在我們可以聊聊事件傳遞了
- 我們都知道android中的事件分發(fā)是從Activity開始分發(fā)的,那么是誰傳遞給Activity的,從Activity又是怎么分發(fā)的呢?
- 假如我們點擊屏幕中的一個按鈕到底是怎么執(zhí)行的匪燕?
再來一張圖:
- 圖片2.png
我們看著上面的圖跟著源碼走一走
WindowManagerService接受到Input事件然后發(fā)送給ViewRootImpl,會執(zhí)行到ViewRootImpl的dispatchInputEvent的方法
public void dispatchInputEvent(InputEvent event) {
dispatchInputEvent(event, null);
}
長話短說:
在ViewRootImpl中最終會調(diào)用DecorView的dispathPointerEvent方法
想看dispathPointerEvent方法源碼的應(yīng)該去View中,因為DecorView類中并沒有dispathPointerEvent方法的源碼,這個方法是存在于View中的
DecorView.java:
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
在dispatchPointerEvent中判斷是否是觸摸事件,如果是自然就到了dispatchTouchEvent方法中
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
上面方法中的cb對象是什么?在Activity的attach方法中創(chuàng)建PhoneWindow的時候又這樣一句代碼
mWindow.setCallback(this);
這個this就是Activity,因為Activity實現(xiàn)了Window.CallBack的接口,所以自然就將實例傳入的PhoneWindow中
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
所以在DecorView的dispatchTouchEvent中會調(diào)用執(zhí)行到Activity的dispatch方法中!
所以在觸摸事件被傳到了Activity的地方,看下上面的圖左邊部分捋一下大概思路就可以喧笔!
接下來就是Activity的事件分發(fā)了
真正的事件分發(fā)是從Activity開始的,上面的只是觸摸事件傳到Activity
現(xiàn)在開始由Activity進行分發(fā)處理
從Activity的dispatchTouchEvent方法開始
- Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
我們看這行代碼:
getWindow().superDispatchTouchEvent(ev)
這里的getWindow()返回的就是在attach中設(shè)置給Activity的PhoneWindow對象
所以就是調(diào)用了PhoneWindow對象的superDispatchTouchEvent的方法
- PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
在PhoneWindow中的superDispatchTouchEvent方法又調(diào)用了mDecor對象的superDispatchTouchEvent方法
這個mDecor自然就是DecorView了,我們前面也說了在PhoneWindow中會創(chuàng)建一個DecorView的對象!
DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
在DecorView的superDispatchTouchEvent方法中調(diào)用了父類的dispatchTouchEvent
注意:是調(diào)用了父類的dispatchTouchEvent帽驯,而不是自己實現(xiàn)的dispatchTouchEvent方法
DecorView的父類是FrameLayout,而FrameLayout中并沒有重寫dispatchTouchEvent方法
應(yīng)為FrameLayout繼承子ViewGroup,所以這里調(diào)用的super.dispatchTouchEvent方法就是ViewGroup的dispatchTouchEvent方法
下來就是ViewGroup的事件分發(fā)方法了
ViewGroup中dispatchTouchEvent的源碼跟以前的相比,加了一些東西,比較復(fù)雜,但是原理都一樣的
代碼就不貼了,所以挑重點的說
----->哈哈
ViewGroup:
兩個主要方法:
onInterceptTouchEvent();事件攔截,即攔截本次事件,不在傳遞
dispatchTouchEvent();事件分發(fā)
首先在dispatchTouchEvent中會判斷是否攔截此次事件,默認為不攔截!
如果不攔截又不是取消事件,則進行遍歷子view尋找符合消費此次事件的view书闸,如果找到符合的則調(diào)用該view的dispatchTouchEvent方法
假如在這個ViewGroup下沒有任何的子View,則調(diào)用此ViewGroup的super.dispatchTouchEvent()方法,
ViewGroup繼承自View,所以就是調(diào)用View的dispatchTouchEvent方法,意思就是把當(dāng)前的ViewGroup當(dāng)作View去處理當(dāng)前的觸摸事件
如果所有的ViewGroup及View都沒有消費此次事件,則進行事件回傳,最終由Activity的onTouchEvent方法進行處理
Activity的dispatch方法
if (getWindow().superDispatchTouchEvent(ev)) {
// 這里表示事件被消費,直接返回
return true;
}
// 事件沒有被消費,由activity的onTouchEvent處理
return onTouchEvent(ev);
事件最終會分發(fā)到我們點擊的那個View上面
那么我們就看看View的事件分發(fā)方法
因為Button繼承自View,所以現(xiàn)在我們看View的dispatchTouchEvent方法,刪除部分代碼,直說關(guān)鍵步驟的
我在代碼中用注釋解釋
View.java
public boolean dispatchTouchEvent(MotionEvent event) {
.....
.....
boolean result = false;
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
ListenerInfo li = mListenerInfo;
// 這里判斷了li和li.mOnTouchListener是否為空
// 這里的li中存儲了我們設(shè)置給View的setOnTouchListener事件
// 也就是我們實現(xiàn)的OnTouchListener,這里執(zhí)行了我們實現(xiàn)的onTouch方法
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// 如果上面的onTouch方法返回false,或者根本沒設(shè)置onTouchlistener事件就會執(zhí)行下面的操作
// 如果result是true,則不執(zhí)行onTouchEvent方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
.....
.....
// 最后返回result
return result;
}
我們看上面的代碼先判斷OnTouchListener是否為null,然后執(zhí)行OnTouchListener的onTouch方法,
如果onTouchListener為null,或者onTouch方法返回false就會執(zhí)行下面的onTouchEvent方法
在onTouchEvent方法中的MotionEvent.ACTION_UP事件中會執(zhí)行performClick();
performClick();也就是我們設(shè)置的setOnClickListener點擊事件
View.java
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
在performClick方法中的li.mOnClickListener其實就是我們設(shè)置的setOnClickListner
和mOnTouchListener一樣都被保存到ListenerInfo類中
在上面代碼中如果我們設(shè)置了OnClickListener,并且用戶進行了點擊操作,那么onClick就會被執(zhí)行了
來張圖