上篇文章中,分析了我之前關(guān)于觸摸事件的一點(diǎn)疑問(wèn),感興趣的夺衍,可點(diǎn)擊觸摸事件之onTouch和onTouchEvent查看
趁著熱乎勁兒,繼續(xù)再來(lái)鞏固下完整的事件分發(fā)流程吧喜命。
先不回憶細(xì)節(jié)沟沙,單純從最簡(jiǎn)單的角度來(lái)看,事件分發(fā)無(wú)非就3步:事件產(chǎn)生->事件傳遞->事件處理壁榕。就跟春晚小品宋丹丹問(wèn)趙大叔把大象放進(jìn)冰箱分幾步一個(gè)道理矛紫。從最原始的角度出發(fā)來(lái)看待這個(gè)問(wèn)題,中間的過(guò)程再逐步細(xì)化护桦,這樣大腦中有個(gè)清晰的流程含衔,分析問(wèn)題也會(huì)順暢的多。
從手指觸摸屏幕的那一刻二庵,觸摸事件便產(chǎn)生了,拋開(kāi)硬件層面的電容電流感應(yīng)缓呛,到應(yīng)用層的APP層面催享,最先肯定是由Activity接收到事件,咱們來(lái)瞧瞧Activity里面的處理過(guò)程哟绊;
public boolean dispatchTouchEvent(MotionEvent ev) {
//...
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
里面有行代碼很關(guān)鍵因妙,“getWindow().superDispatchTouchEvent(ev)”,事件由window分發(fā)票髓,如果返回true,后面的onTouchEvent將不執(zhí)行攀涵,怎么感覺(jué)這句話(huà)很熟悉?洽沟?原來(lái)上篇文章剛分析過(guò)類(lèi)似的以故。繼續(xù)追查這個(gè)window是什么,原來(lái)是:
mWindow = new PhoneWindow(this, window, activityConfigCallback);
熟悉安卓界面加載的都知道裆操,phoneWindow跟DecorView密切相關(guān)怒详,莫非window將事件傳給了DecorView,繼續(xù)看源碼:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
果然踪区,一切盡在預(yù)料之中啊昆烁,這種感覺(jué)很爽。DecorView中分發(fā)過(guò)程也很簡(jiǎn)單缎岗,
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
DecorView是一個(gè)FrameLayout布局静尼,它由上下兩部分組成,上面是actionBar,下面是我們最最親愛(ài)的在setContentView()方法中傳進(jìn)xml布局文件鼠渺,生成的視圖組鸭巴。上面的事件已經(jīng)分發(fā)至DecorView了,現(xiàn)在我們將樣式設(shè)為為noActionBar系冗,那么DecorView布局中就只有一個(gè)contentView布局奕扣。在上面的方法中,由于FrameLayout沒(méi)有重寫(xiě)分發(fā)方法掌敬,所以會(huì)接著向上查找分發(fā)方法惯豆,最終找到ViewGroup中的dispatchTouchEvent方法,而這個(gè)viewGroup中的第一個(gè)子view就是contentView生成的視圖組奔害。接下來(lái)看看viewGroup中的分發(fā)方法楷兽。
for (int i = childrenCount - 1; i >= 0; i--) {
//...
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
}
//...
}
代碼很長(zhǎng),挑關(guān)鍵的看华临,看到for循環(huán)芯杀,知道重頭戲來(lái)了,我們知道DecorView作為所有Activity根視圖的外層容器雅潭,一個(gè)Activity界面就是有一個(gè)個(gè)ViewGroup不斷包含子View構(gòu)成的揭厚,在ViewGroup里對(duì)所有子view進(jìn)行遍歷,肯定也會(huì)遍歷傳遞處理觸摸事件扶供,我們找child和touchEvent兩個(gè)關(guān)鍵字筛圆,找到一個(gè)方法
“dispatchTransformedTouchEvent”,進(jìn)去看一下:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
//...
}
期待已久的child.dispatchTouchEvent(event)出現(xiàn)了4慌ā太援!出現(xiàn)了!扳碍!截止到目前為止提岔,根據(jù)我們所掌握的信息,可以很肯定的是笋敞,
根據(jù)child類(lèi)型的不同振亮,dispatchTouchEvent實(shí)現(xiàn)肯定也不同,view類(lèi)型的不細(xì)說(shuō)了鞭莽,直接貼代碼吧:
public boolean dispatchTouchEvent(MotionEvent event) {
//...
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
//...
}
ViewGroup相比于View肯定要復(fù)雜些坊秸,其內(nèi)部多了要處理子view的情況,在這里貼上一位網(wǎng)友制作的圖澎怒,個(gè)人認(rèn)為很不錯(cuò):
途中很清晰的描繪了viewgroup是如何把事件傳遞給子view的褒搔,onInterceptTouchEvent是viewGroup中獨(dú)有的方法阶牍,它的返回值直接決定了是否會(huì)將事件傳遞給子view處理。如果子 View 不處理星瘾,這個(gè)“鍋”就得 ViewGroup 自己擔(dān)著走孽。所以事件會(huì)傳遞到 super.dispatchTouchEvent()。ViewGroup 類(lèi)繼承自 View 類(lèi)琳状,也就是進(jìn)入了前文說(shuō)的 View 事件分發(fā)流程磕瓷,就相當(dāng)于詢(xún)問(wèn)當(dāng)前 ViewGroup 自己是否處理這個(gè)事件,細(xì)節(jié)這里就不重復(fù)了念逞。如果當(dāng)前 ViewGroup自己處理了困食,對(duì)于上級(jí) ViewGroup 而言,還是找到了 target翎承,如果當(dāng)前 ViewGroup 不處理硕盹,這個(gè)“鍋”繼續(xù)拋給上級(jí) ViewGroup。
當(dāng)最外層的子view接收到分發(fā)事件時(shí)叨咖,會(huì)進(jìn)入它自身的dispatchTouchEvent方法瘩例,當(dāng)它不攔截時(shí),事件會(huì)繼續(xù)進(jìn)入到onTouchEvent方法中處理甸各,然后再一級(jí)一級(jí)向上傳遞返回值垛贤。
以上就是觸摸事件分發(fā)流程的簡(jiǎn)要分析,源碼也一直在更新趣倾。掌握了主要的流程南吮,任他怎么改,也能做到心中有數(shù)誊酌。