轉(zhuǎn)載好文章:
成都創(chuàng)新互聯(lián)于2013年開始,先為烏拉特前等服務(wù)建站,烏拉特前等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為烏拉特前企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
http://blog.csdn.net/chunqiuwei/article/details/41084921
學(xué)了這么久的Android,面試也常被問到事件分發(fā)機(jī)制,但總感覺對這個機(jī)制還是不清不楚.突然之間
翻了一下李剛的
一、監(jiān)聽與回調(diào)
第一,要明白和事件相關(guān)的3個概念:事件源,事件,事件監(jiān)聽器.
事件源:就是事件作用的對象
事件:就是事件本身,可以是點(diǎn)擊,長按,移動等等,就是XxxEvent.
事件監(jiān)聽器:就是Listener,一般寫成內(nèi)部類的形式,說白了,就是一個類,而這個類往往是事件源
內(nèi)部的接口實(shí)現(xiàn)類。
第二,
監(jiān)聽:就是給控件添加一個監(jiān)聽器,即addXxxListener(new XxxListener(){...}),事件源本身不直
接處理,而是交由事件監(jiān)聽器進(jìn)行處理.
回調(diào):當(dāng)事件作用在事件源的時(shí)候,事件源本身會觸發(fā)一些自己的方法,自己來處理事件.但往往事件
源的這些方法是封裝在事件源對象的內(nèi)部的,用戶看不見.這也就是為什么需要有監(jiān)聽器的原
因,讓用戶知道事件源發(fā)生了什么事件.通常在自定義控件的時(shí)候,就需要復(fù)寫和Touch相關(guān)的
事件,同時(shí)抽取監(jiān)聽器接口,讓用戶去實(shí)現(xiàn).
-----------------------------------------------------------------------------------------
舉個小例子:
假如有這樣一個需求:點(diǎn)擊一下Button,打印一條日志.
那么有2種實(shí)現(xiàn)方式
第1種實(shí)現(xiàn)方式-為Button設(shè)置監(jiān)聽器
Button.setOnClickListener(new OnClickListener(){
onClick(View view)
{
Log...
}
});
第2種實(shí)現(xiàn)方式-繼承Button,自定義一個MyButton
定義的時(shí)候,復(fù)寫B(tài)utton的onTouchEvent方法,在里面打印日志.
那么,現(xiàn)在又有一個需求:點(diǎn)擊一下Button,彈出一個吐司.
如果采用第1種方式,只要修改一下onClick里的代碼.
如果采用第2種方式,那么1種辦法是再創(chuàng)建一個Button的子類,另外一個方法就是修改原來
MyButton的onTouchEvent方法,但是這樣一來又不能滿足開始的需求了.
所以到這里,監(jiān)聽與回調(diào)的區(qū)別也就不言而喻了吧.
1)監(jiān)聽只是組件的作者在寫回調(diào)方法時(shí),暴露給用戶的一個接口,這樣用戶可以實(shí)現(xiàn)接口,
達(dá)到自身的需求,最終用戶實(shí)現(xiàn)的接口里的方法在組件的回調(diào)方法里會被回調(diào)。
二、結(jié)合2.3.3中View的源碼,再談監(jiān)聽與回調(diào)
1.View的dispatchTouchEvent方法
我之前一直沒有搞清楚View的dispatchTouchEvent的返回值的作用,現(xiàn)在明白了,
返回true,作用在View上的觸摸事件(包括Button的點(diǎn)擊事件)就會生效;
返回false,作用在View上的觸摸事件就失效。
但具體dispatchTouchEvent方法又在哪里被調(diào)用了呢(?????????????)
public boolean dispatchTouchEvent(MotionEvent event) { 。。。 if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
2)監(jiān)聽是先判斷的,所以監(jiān)聽比回調(diào)的優(yōu)先級要高。先判斷l(xiāng)istener.onTouch的返回值
,否則就return onTouchEvent(event)將事件交由組件的onTouchEvent回調(diào)方法進(jìn)行處理。
所謂View的事件分發(fā),解決的問題就是事件是由用戶處理還是View自身處理。
三、結(jié)合2.3.3中ViewGroup的源碼,再談監(jiān)聽與回調(diào)
由于ViewGroup的dispatch事件源碼過多,我就不一一寫了,大致地去描述一下。
ViewGroup的dispatch事件干的事:
先弄一個變量來緩存可能會捕捉并處理事件的View,target.
在點(diǎn)擊ev.getAction == MotionEvent.Action_Down的事件里
根據(jù)onIntercept事件判斷自己需不需要攔截事件,如果不攔截,則遍歷ViewGroup里的
每個子View,通過計(jì)算子View的矩形區(qū)域來判斷點(diǎn)擊事件的坐標(biāo)有沒有落在子View上,如果點(diǎn)
擊事件的坐標(biāo)落在子View并且子View捕捉并處理事件,那么target就等于這個子View。
判斷target是否為null
如果為null,則調(diào)用ViewGroup的父類的dispatch事件,也就是把ViewGroup當(dāng)作
View來處理。
如果不為null,則交由target.dispatch事件進(jìn)行處理。
所謂ViewGroup的事件分發(fā),解決的問題就是事件到底是由哪個View來處理。
四、FrameLayout的事件分發(fā)機(jī)制
我們都知道,ViewGroup的事件傳遞順序是由父控件往子控件傳遞,但是假如是FrameLayout的兩
個子控件重疊在一起,并且兩個子控件沒有包含(父子)關(guān)系,那么事件的傳遞順序是怎樣的呢
其實(shí)這個Android源碼里已經(jīng)說得十分地清楚了,
// Scan children from front to back.
也就是從上往下查找,自然事件也是從上往下傳遞。
1)在做《風(fēng)口》項(xiàng)目時(shí),我也遇到過這種問題:
明明點(diǎn)擊的地方什么也沒有,但是事件卻被響應(yīng)了。我將布局看來看去,想了很久,突然
明白這就是事件分發(fā)導(dǎo)致的。FrameLayout的上層Layout點(diǎn)擊的位置沒有控件響應(yīng)事件,但是在下
一層的Layout中,鼠標(biāo)點(diǎn)位的位置有控件響應(yīng)。這種解釋了這種見鬼的原因!
2)小資錢包首頁,“馬上搶購”按鈕和它所在的父布局響應(yīng)同樣的事件。
第1種方法:給2者注冊同一個監(jiān)聽器
第2種方法:屏蔽Button對事件的響應(yīng),只注冊父布局的監(jiān)聽。(即怎么讓按鈕可點(diǎn)擊但是不響
應(yīng)點(diǎn)擊事件)
復(fù)寫B(tài)utton onIntercept方法返回false.