2019-01-16 android之View和ViewGroup事件分發(fā)機(jī)制分析(一)(View的事件分發(fā)機(jī)制)

轉(zhuǎn)自? ??https://blog.csdn.net/gsw333/article/details/51995391

一個(gè)View和ViewGroup都有自己的事件分發(fā)機(jī)制,都是寫好了的,可能有些人就問了,既然都寫好了,那還需要學(xué)習(xí)什么?直接用不就行了。沒錯(cuò),我給你做了一碗可以吃的便便,然后你不用自己做了,直接吃,但是你又嫌棄便便味道不好吃,那你咋搞?當(dāng)然自己做了!那不就行了,存在即合理~雞汁~如果能認(rèn)真看完我這篇文章,相信肯定能對(duì)View的事件分發(fā)機(jī)制很了解了。

事件分發(fā)機(jī)制根據(jù)View和ViewGroup的不同所處理的方式也不同:

a.dispatchTouchEvent() :用來進(jìn)行事件的分發(fā),它的返回結(jié)果表示是否消耗掉當(dāng)前的事件,但是它的返回結(jié)果也是受下面兩個(gè)方法的影響

b.onInterceptTouchEvent():用來表示是否攔截當(dāng)前事件,即是否將事件傳遞給下一層

c.onTouchEvent():這個(gè)方法應(yīng)該都很熟悉了,對(duì)事件的處理,比如手指按下,移動(dòng),抬起,對(duì)這些動(dòng)作的處理

ViewGroup是a.b.c三個(gè)方法都有,而View只有a.c 兩個(gè)方法,至于原因顯而易見了吧。

首先,我們先來看View的事件分發(fā)機(jī)制,我們自定義View繼承ImageView(代碼片段1):


我們現(xiàn)在看看打印結(jié)果:


從結(jié)果我們可以看出來一件事:那就是View的事件的這兩個(gè)方法是先執(zhí)行dispatchTouchEvent方法再執(zhí)行onTouchEvent 方法的,dispatchTouchEvent是用來分發(fā)事件用的,執(zhí)行它,此時(shí),如果它直接返回false,那么它就不消費(fèi)事件,那么它就不會(huì)執(zhí)行onTouchEvent方法了,因?yàn)橐呀?jīng)返回false表示它不需要消費(fèi)touch事件,我們將dispatchTouchEvent方法改為如下進(jìn)行測(cè)試(代碼片段2):


打印結(jié)果就是:


我再說下,dispatchTouchEvent方法的返回值表示是否消費(fèi)當(dāng)前事件,它是用來事件分發(fā)的,相信看上面代碼能看出來了,執(zhí)行了dispatchTouchEvent里面對(duì)ACTION_DOWN直接返回false代表不消費(fèi)不進(jìn)行事件的分發(fā),所以就不會(huì)繼續(xù)super.dispatchTouchEvent()了,更別說onTouchEvent方法了,不會(huì)進(jìn)行任何下一步了。如果我們沒有直接返回false而是直接break,那么就沒有提前結(jié)束這個(gè)方法,因?yàn)槟阒苯觬etrun后方法就直接結(jié)束了,所以沒有return的時(shí)候,下面就super.dispatchTouchEvent,這個(gè)里面做了些什么呢?說實(shí)話,我也沒怎么看源碼,畢竟我也是渣渣,它里面你可以這么認(rèn)為,它里面做的操作就是:受到onTouchEvent方法的影響,什么意思呢?咋們不是沒有直接return嗎?所以此時(shí)dispatchTouchEvent方法還沒有進(jìn)行值的返回,那沒有return時(shí)的返回值就是onTouchEvent的返回值了,如果onTouchEvent返回true就是代表當(dāng)前View消費(fèi)touch事件了,false就是代表不消費(fèi)。一句話總結(jié)就是:dispatchTouchEvent方法表示是否消費(fèi)事件,如果dispatchTouchEvent直接返回false那么就是不消費(fèi),我都不消費(fèi)了還需要分發(fā)事件么?不需要,所以結(jié)束;如果沒有返回那就代表需要進(jìn)行事件分發(fā),只是分發(fā)!分發(fā)給當(dāng)前View予touch事件,但是消不消費(fèi)就是根據(jù)onTouchEvent來決定了,它返回true就代表消費(fèi),那么dispatchTouchEvent就返回true。但是,細(xì)心的人會(huì)問了,我的onTouchEvent中ACTION_MOVE和ACTION_UP沒有返回false,那為啥沒打印執(zhí)行呢?因?yàn)槲覀冎牢覀冞M(jìn)行touch事件的時(shí)候,手指是先按下、移動(dòng)、抬起的,所以touch事件的順序是ACTION_DOWN, ACTION_MOVE, ACTION_UP。所以onTouchEvent方法里面第一個(gè)進(jìn)行判斷是事件是ACTION_DOWN,里面的判斷是執(zhí)行l(wèi)og打印,然后return false了,ACTION_DOWN的打印出來了再進(jìn)行return的。那有人問了,ACTION_MOVE和ACTION_UP也是先log再return的啊,為啥它們就不打印Log了?因?yàn)閂iew的事件處理中,如果第一個(gè)動(dòng)作ACTION_DOWN不消費(fèi),那么其他的后續(xù)動(dòng)作事件ACTION_MOVE之類的都不會(huì)再傳過來給它了,所以才會(huì)沒有后續(xù)的執(zhí)行打印。

上面已經(jīng)詳細(xì)說明了dispatchTouchEvent和onTouchEvent方法之間的關(guān)系了,下面用一段代碼來試試(代碼片段3):


注意看上面的代碼,我們讓dispatchTouchEvent方法無論什么動(dòng)作ACTION_DOWN還是ACTION_MOVE等等,全都返回false,那么按照我上面說的,返回false表示不消費(fèi)事件,所以onTouchEvent應(yīng)該是不執(zhí)行的,那么我們看看打印結(jié)果對(duì)不對(duì):

返回結(jié)果是只有一個(gè)dispatchTouchEvent ?DOWN,沒有執(zhí)行onTouchEvent任何動(dòng)作,所以印證上面我說的對(duì)了。而且也只打印了DOWN事件,也說明了我上面說的:如果不消費(fèi)DOWN事件,那么后續(xù)的都不會(huì)給它了。

注意:如果我們將所有方法都用默認(rèn)的,不自己return false,即(代碼片段1)一樣,打印結(jié)果上面已經(jīng)試過,是:


我們示例是繼承的ImageView,如果我們改為TextView你就會(huì)發(fā)現(xiàn)打印結(jié)果是:

這是為什么呢?因?yàn)閂iew的onTouchEvent默認(rèn)會(huì)消費(fèi)事件(前提是它的clickable和longclickable不同時(shí)為false),所有View的longClickable都是默認(rèn)false,但是clickable就不一定了,ImageView的clickable默認(rèn)是true,而TextView的clickable默認(rèn)是false,即TextView的clickable和longclickable同時(shí)為false了,那么它默認(rèn)不會(huì)消費(fèi)事件了,所以它的onTouchEvent中的DOWN動(dòng)作是返回false的,默認(rèn)不消費(fèi),所以才沒有后續(xù)的UP事件。

好了,上面對(duì)View中的兩個(gè)方法的說明已經(jīng)很詳細(xì)了,相信看完后能理解我所說的了,那么下面我們接下來分析一下View的onTouch方法和onTouchEvent方法的關(guān)系,onTouch方法就是setOnTouchListener里面所重寫的onTouch方法,我們來重寫并默認(rèn)執(zhí)行看看(代碼片段4):


上面是onTouch方法,下面是TouchView里面的dispatchTouchEvent方法和onTouchEvent方法(代碼片段5)


我們獲取布局中用的我們上面寫的TouchView,然后給它setOnTouchListener,然后現(xiàn)在打印結(jié)果:

看打印結(jié)果,先執(zhí)行的是dispatchTouchEvent 方法,這個(gè)不用解釋了,因?yàn)樗沁M(jìn)行事件分發(fā)的,無論是onTouch還是onTouchEvent都需要先被分發(fā)事件才能處理事件,所以先執(zhí)行dispatchTouchEvent 可以理解。然后發(fā)現(xiàn)onTouchEvent方法居然比onTouch方法后執(zhí)行,因?yàn)榇_實(shí)是這樣的,可以看源代碼,先執(zhí)行onTouch再執(zhí)行onTouchEvent方法的,因?yàn)樵创a中onTouchEvent方法執(zhí)行的前提是對(duì)onTouch方法的返回值做判斷,如果返回值是false,那么就會(huì)進(jìn)入onTouchEvent方法中;如果返回值是true,那么就不會(huì)進(jìn)入,因?yàn)闀?huì)認(rèn)為事件被onTouch方法消費(fèi)了,所以不再執(zhí)行了,結(jié)論就是:onTouch方法的優(yōu)先級(jí)高于onTouchEvent方法。如果想看源碼分析的,就去看看郭神和洪洋大神的吧,他們博客做了詳細(xì)的源碼分析,我就不寫了,畢竟我不是郭神他們啊~菜鳥路過~

上面已經(jīng)說了onTouch方法優(yōu)先于onTouchEvent方法,做了兩個(gè)方法的聯(lián)系分析,下面再來分析一下onclick事件,有人會(huì)問了,onclick事件和touch事件難道還有關(guān)系?其實(shí)想一想就知道,肯定有關(guān)系了,因?yàn)槟鉶nclick不就是觸摸按鈕么,點(diǎn)擊一下就是一個(gè)ACTION_DOWN和ACTION_UP事件。對(duì)于onclick事件,它們的優(yōu)先級(jí)關(guān)系是:

onTouch > onTouchEvent >onclick事件,為什么呢?因?yàn)閛nclick事件的執(zhí)行就是在onTouchEvent里面的,下面我們看看代碼(代碼片段6):


設(shè)置下onClick事件,然后打印結(jié)果:


發(fā)現(xiàn)調(diào)用順序是dispatchTouchEvent - onTouch - onTouchEvent - onclick,那么我上面說的onclick事件的執(zhí)行就是在onTouchEvent方法里面就能說得過去了,其實(shí)源碼里面onclick事件的處理是在onTouchEvent方法中的ACTION_UP的處理當(dāng)中的,所以才說onclick事件在onTouchEvent中進(jìn)行的。不要以為完事了,記得上面我說過的話吧:

View的onTouchEvent默認(rèn)會(huì)消費(fèi)事件(前提是它的clickable和longclickable不同時(shí)為false),所有View的longClickable都是默認(rèn)false,但是clickable就不一定了,ImageView的clickable默認(rèn)是true,而TextView的clickable默認(rèn)是false,即TextView的clickable和longclickable同時(shí)為false了,那么TextView它默認(rèn)不會(huì)消費(fèi)事件了,所以它的onTouchEvent中的DOWN動(dòng)作是返回false的,默認(rèn)不消費(fèi),所以才沒有后續(xù)的UP事件。

那么按照我說的,(記住我說的兩個(gè)點(diǎn):1.TextView的onTouchEvent方法默認(rèn)返回false不消費(fèi)事件;2.onClick事件的處理是在onTouchEvent方法里面的ACTION_UP動(dòng)作處理里面的。那么按照我說的,如果ACTION_UP動(dòng)作不執(zhí)行,onclick事件就不會(huì)觸發(fā)了?),現(xiàn)在我們將(代碼片段1)繼承ImageView改為繼承TextView,然后再設(shè)置onclick事件即setOnClickListener:



然后打印結(jié)果:


我們看看結(jié)果,

第一個(gè)問題:TextView的onTouchEvent方法默認(rèn)返回false不消費(fèi)的,但是為啥DOWN和UP動(dòng)作都執(zhí)行了,返回的true呢?

第二個(gè)問題:為什么執(zhí)行了onclick方法,TextView的onTouchEvent方法默認(rèn)返回false,那么UP就不會(huì)執(zhí)行,為啥還有onclick?

這兩個(gè)問題的原因都是一個(gè):因?yàn)槲覀冊(cè)O(shè)置了onclick事件即setOnclickListener,它會(huì)讓當(dāng)前View的clickable變?yōu)閠rue,所以那么View的onTouchEvent方法就會(huì)默認(rèn)返回true了(不懂的自己回憶我前面說過的:View的onTouchEvent默認(rèn)會(huì)消費(fèi)事件(前提是它的clickable和longclickable不同時(shí)為false)),所以現(xiàn)在一切都能解釋通了。

關(guān)于View的事件分發(fā)機(jī)制就先說到這里了,差不多都說完了,應(yīng)該很清晰了,如果看完了這篇文章,相信View的事件分發(fā)機(jī)制就明白了,至于要看源碼解析的就去看郭神和洪洋大神的博客吧,我就懶得重復(fù)他們的了。ViewGroup的事件分發(fā)機(jī)制和View的不一樣我說過的,多一個(gè)onInterceptTouchEvent方法,關(guān)于ViewGroup的事件分發(fā)機(jī)制的分析我會(huì)在后面的文章進(jìn)行說明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容