ViewGroup的事件分發(fā)機制

這兩天的文章都是直接正如主題辖所,沒有什么開頭的描述的缘回,因為剛開始寫文章,也不知道說什么啦吧。啊拙寡,今天天氣不錯哈。

那么今天呢我講一下這個View的事件分發(fā)機制褒墨,這個很多面試的時候都會問到擎宝,因為這是View的一個核心點绍申,對View的點擊事件的一個分發(fā)的機制,所謂的事件分發(fā)機制呢胃碾,其實就是用戶對這個View進行觸碰產(chǎn)生的一個運動事件筋搏,比如說:點擊事件,長按事件這些俄周,這個事件產(chǎn)生了以后呢系統(tǒng)要把這個事件傳遞給一個View來進行消費髓迎,而這個傳遞的過程就是分發(fā)過程排龄。事件分發(fā)過程是由三個方法來完成的

public boolean dispatchTouchEvent(MotionEvent ev)

用來進行事件分發(fā),只要你觸碰了任何控件尺铣,都會調(diào)用這個方法,主要是判斷是當(dāng)前view消費還是往下級view傳遞。

public boolean onInterceptTouchEvent(MotionEvent ev)

這個方法用來判斷是否攔截這個事件澈灼,默認為false也就是不攔截侄非,需要進行攔截的時候在dispatchTouchEvent內(nèi)部調(diào)用逞怨。

public boolean onTouchEvent(MotionEvent event)

用來處理當(dāng)前事件福澡,返回結(jié)果表示是否消費,如果不消費除秀,則這個事件不會再接收到算利。

那么他們?nèi)齻€有什么不可描述的關(guān)系呢,首先我們要先知道一個點擊事件產(chǎn)生后的傳遞過程暂吉,當(dāng)一個事件產(chǎn)生一定會先傳遞給當(dāng)前Activity慕的,然后Activity再傳遞給window挤渔,window會傳遞給當(dāng)前頁面的頂級View,最后這個View接收到事件以后再按照事件分發(fā)機制去分發(fā)事件嫉父。這里有一種情況就是眼刃,如果一個View的onTouchEvent返回了false鸟整,那么他的父容器的onTouchEvent也會被調(diào)用,以此類推弟头,所有的View都不消費當(dāng)前事件的話,那么最終這個事件會傳遞給Activity疹娶,Activity的onTouchEvent會進行消費掉伦连。這里采用一個很好的例子,是從別的書籍上看到的额港,一個領(lǐng)導(dǎo)分配了一個很難的任務(wù)給你歧焦,然后你不會(onTouchEvent返回false)绢馍,那么怎么辦呢,只能交回給上級(上級的onTouchEvent被調(diào)用)猖任,那如果你的上級也搞不定呢瓷耙,那就交給上級的上級哺徊,到最后最頂級的上級那里,進行消費掉盈滴。這個例子是不是很貼近生活轿钠,哈哈我也是從別得地方看到的。

那么我講了這么半天大家也肯定還不是很懂症汹,那么我們從源碼入手進行分析背镇,打開你們的studio,找到Activity破婆,在Activity里面有個方法叫

public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if(getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}

通過上面的代碼我們可以看出來胸囱,Activity將時間交給附屬的Window進行分發(fā)烹笔,如果返回true的話,那么事件循環(huán)就結(jié)束了饰豺,如果返回false的話就
代表著事件沒人處理柬帕,那么最后會交給Activity的onTouchEvent處理陷寝。

那么我接著Window往下看其馏,Window是怎么處理這個事件的呢,我們看源碼知道了Window是個抽象類仔引,抽象類怎么處理呢褐奥?所以我們要找他Window的實現(xiàn)類PhoneWindow,我們找到PhoneWindow的dispatchEvent方法

@Override
public boolean superDispatchTrackballEvent(MotionEvent event) {
return mDecor.superDispatchTrackballEvent(event);
}

通過上面的代碼我們可以看到PhoneWindow把事件傳遞給了mDecor撬码,那么這個mDecor是什么呢,我繼續(xù)往下看

// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;

那么我們可以看到夫否,mDecor是DecorView凰慈,那么這個DecorView又是什么呢驼鹅,注意看上面的注釋森篷,翻譯過來就是,這是窗口的頂級視圖疾宏,包含窗口裝飾触创。也就是說,PhoneWindow把事件傳遞給了DecorView也就是頂級視圖岩馍,而DecorView是繼承FrameLayout蛀恩,而FrameLayout又是繼承的Viewgroup茂浮,也就是說,事件一定會傳遞到Viewgroup里面去顽馋,所以我們直接去看Viewgroup的dispatchTouchEvent是怎么處理的寸谜。

// Check for interception.

final booleanintercepted;

if(actionMasked == MotionEvent.ACTION_DOWN||mFirstTouchTarget!=null) {

final booleandisallowIntercept = (mGroupFlags&FLAG_DISALLOW_INTERCEPT) !=0;

if(!disallowIntercept) {

intercepted = onInterceptTouchEvent(ev);

ev.setAction(action);// restore action in case it was changed

}else{

intercepted =false;

}

}else{

// There are no touch targets and this action is not an initial down

// so this view group continues to intercept touches.

intercepted =true;

}

Viewgroup的dispatchTouchEvent的代碼邏輯比較多熊痴,我們就分段說明聂宾,先看上面這段代碼,上面注釋Check for interception.表示的意思是檢查攔截巾陕,也就是這段代碼的邏輯是是否攔截處理這個事件惜论,然后我們從代碼中可以看出止喷,Viewgroup在兩種情況下會判斷是否攔截事件,那兩種呢乾巧,一個是MotionEvent.ACTION_DOWN也就是按下的時候沟于,還有一個是mFirstTouchTarget!=null的時候,那這個是什么情況呢展懈,這里我們可以從后面的代碼中可以看出來供璧,在后面的代碼中我們可以看出如果ViewGroup的子元素處理成功時睡毒,mFirstTouchTarget會被賦值并指向子元素,也就是當(dāng)Viewgroup不攔截事件交由子元素處理時供搀,mFirstTouchTarget 钠至!=null棕洋,反過來說掰盘,ViewGroup如果攔截了當(dāng)前事件的話赞季,那么(actionMasked == MotionEvent.ACTION_DOWN||mFirstTouchTarget!=null)這個判斷就會不成立返回false,導(dǎo)致ViewGroup中的onInterceptTouchEvent不會再被調(diào)用次绘,并且同一事件系列的其他事件也會默認交給他處理邮偎。

我們接著再看ViewGroup不攔截事件交由子元素處理的時候义黎,看源碼:

finalView[] children =mChildren;

for(inti = childrenCount -1; i >=0; i--) {

final intchildIndex = getAndVerifyPreorderedIndex(

childrenCount, i, customOrder);

finalView child =getAndVerifyPreorderedView(

preorderedList, children, childIndex);

// If there is a view that has accessibility focus we want it

// to get the event first and if not handled we will perform a

// normal dispatch. We may do a double iteration but this is

// safer given the timeframe.

if(childWithAccessibilityFocus !=null) {

if(childWithAccessibilityFocus != child) {

continue;

}

if(!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child,null)) {

ev.setTargetAccessibilityFocus(false);

continue;

}

newTouchTarget = getTouchTarget(child);

if(newTouchTarget !=null) {

// Child is already receiving touch within its bounds.

// Give it the new pointer in addition to the ones it is handling.

newTouchTarget.pointerIdBits|= idBitsToAssign;

break;

}

resetCancelNextUpFlag(child);

if(dispatchTransformedTouchEvent(ev,false, child, idBitsToAssign)) {

// Child wants to receive touch within its bounds.

mLastTouchDownTime= ev.getDownTime();

if(preorderedList !=null) {

// childIndex points into presorted list, find original index

for(intj =0; j < childrenCount; j++) {

if(children[childIndex] ==mChildren[j]) {

mLastTouchDownIndex= j;

break;

}

}

}else{

mLastTouchDownIndex= childIndex;

}

mLastTouchDownX= ev.getX();

mLastTouchDownY= ev.getY();

newTouchTarget = addTouchTarget(child, idBitsToAssign);

alreadyDispatchedToNewTouchTarget =true;

break;

}

// The accessibility focus didn't handle the event, so clear

// the flag and do a normal dispatch to all children.

ev.setTargetAccessibilityFocus(false);

}

if(preorderedList !=null) preorderedList.clear();

}

上面這段代碼呢艇拍,我們可以看出來在遍歷ViewGroup所有的子元素宠纯,然后通過子元素是否在播動畫和點擊事件的坐標(biāo)是否在子元素的區(qū)域內(nèi)來判斷子元素是否能夠接受這個事件。如果這兩個條件都滿足就交給這個子元素處理快集,我們看到dispatchTransformedTouchEvent這個方法廉白,點擊進去看蒙秒,你會發(fā)現(xiàn)

if(child ==null) {

handled =super.dispatchTouchEvent(event);

}else{

handled = child.dispatchTouchEvent(event);

}

他在判斷child也就是子元素是否為空晕讲,不為空的話就交給子元素處理了,從而完成了一輪事件分發(fā)弄息。如果子元素的dispatchTouchEvent返回true勤婚,這時我們暫時不考慮事件在子元素內(nèi)部是怎么處理的馒胆,那么mFirstTouchTarget就會被賦值同時跳出for循環(huán),看源碼

newTouchTarget= addTouchTarget(child, idBitsToAssign);

alreadyDispatchedToNewTouchTarget =true;

break;

這里完成了mFirstTouchTarget的賦值并終止對子元素的遍歷睦尽。吐過dispatchTouchEvent返回false型雳,ViewGroup就會把事件分發(fā)給下一個子元素(如果還有下一個子元素的話)当凡。

看到上面,你們一定會想mFirstTouchTarget是在哪里賦值的啊纠俭,我們看addTouchTarget這個方法

privateTouchTarget addTouchTarget(@NonNull View child,intpointerIdBits) {

finalTouchTarget target = TouchTarget.obtain(child, pointerIdBits);

target.next=mFirstTouchTarget;

mFirstTouchTarget= target;

returntarget;

}

mFirstTouchTarget是一種單鏈表結(jié)構(gòu)沿量,mFirstTouchTarget是否被賦值直接影響到ViewGroup對事件的攔截,如果mFirstTouchTarget為空冤荆,那么ViewGroup將默認攔截同一序列中的所有點擊事件朴则。

今天就先講ViewGroup的事件分發(fā),如果大家覺得還是不理解的話匙赞,就去按照郭神的例子敲一下佛掖,這里附上一個連接:http://blog.csdn.net/guolin_blog/article/details/9153747

謝謝大家對我的支持妖碉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芥被,一起剝皮案震驚了整個濱河市欧宜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拴魄,老刑警劉巖冗茸,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異匹中,居然都是意外死亡夏漱,警方通過查閱死者的電腦和手機亡鼠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門鲜滩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來整葡,“玉大人欧聘,你說我怎么就攤上這事“竦” “怎么了担敌?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵檀何,是天一觀的道長重虑。 經(jīng)常有香客問我践付,道長,這世上最難降的妖魔是什么缺厉? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任永高,我火速辦了婚禮,結(jié)果婚禮上提针,老公的妹妹穿的比我還像新娘命爬。我一直安慰自己,他們只是感情好关贵,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布遇骑。 她就那樣靜靜地躺著卖毁,像睡著了一般揖曾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亥啦,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天炭剪,我揣著相機與錄音,去河邊找鬼翔脱。 笑死奴拦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的届吁。 我是一名探鬼主播错妖,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼绿鸣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暂氯?” 一聲冷哼從身側(cè)響起潮模,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痴施,沒想到半個月后擎厢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡辣吃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年动遭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片神得。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡厘惦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哩簿,到底是詐尸還是另有隱情绵估,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布卡骂,位于F島的核電站国裳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏全跨。R本人自食惡果不足惜缝左,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浓若。 院中可真熱鬧渺杉,春花似錦、人聲如沸挪钓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碌上。三九已至倚评,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間馏予,已是汗流浹背天梧。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留霞丧,地道東北人呢岗。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親后豫。 傳聞我的和親對象是個殘疾皇子悉尾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容