android触摸消息的派发过程
1.觸摸消息是消息獲取模塊直接派發(fā)給應(yīng)用程序的。
2.觸摸消息在處理時(shí), 需要根據(jù)觸摸坐標(biāo)計(jì)算該消息應(yīng)該派發(fā)給哪個(gè)View/ViewGroup, 在案件取消處理中不存在 該計(jì)算過程。
3.沒有類似”系統(tǒng)按鍵”的”系統(tǒng)觸摸鍵”, 應(yīng)用程序可完全控制觸摸行為。
4.子視圖優(yōu)先父視圖處理消息, 即首先是子視圖處理該消息,只有當(dāng)子視圖消耗該消息時(shí), 父視圖才有機(jī)會處理 該消息。
觸摸消息總體派發(fā)過程
1.進(jìn)行物理像素到邏輯像素的轉(zhuǎn)換。
2.如果是DOWN消息, 調(diào)用ensureTouchMode(true)函數(shù)則進(jìn)入觸摸模式,與之相反的是”非觸摸模式”,即按鍵模式。
3.將屏幕坐標(biāo)轉(zhuǎn)換到視圖坐標(biāo)。
4.調(diào)用mView.dispathTouchEvent()將消息派發(fā)給根視圖,該函數(shù)內(nèi)部會繼而將消息派發(fā)到整個(gè)View樹。
5.如果以上根視圖以及其所有子視圖都沒有消耗該消息,最后處理屏幕邊界偏移。屏幕邊界偏移在程序中用英文edge slop表示,它的作用是當(dāng)用戶正好觸摸到屏幕邊界時(shí),系統(tǒng)自動對原始消息進(jìn)行一定的偏移,然后在新的偏移后的位置上尋找是否有匹配的視圖,如果有則將消息派發(fā)到該視圖。
根視圖內(nèi)部消息派發(fā)過程
首先來看mView.dispatchTouchEvent()的派發(fā)過程。該函數(shù)是在ViewRoot中調(diào)用的,mView的類型可能有兩種情況,對于應(yīng)用窗口而言,mView是一個(gè)PhoneWindow中的DecorView類型,對于非應(yīng)用窗口而言,mView是一般的ViewGroup類型。
在DecorView中,首先判斷是否存在Callback對象,它和按鍵消息派發(fā)時(shí)的Callback對象一樣,就是Activity類。如果沒有Callback對象,則直接調(diào)用DecorView基類ViewGroup中的dispatchTouchEvent()函數(shù)。
在Activity中,dispatchTouchEvent()的過程如下。
1、如果ACTION_DOWN消息,則調(diào)用onUserInteraction()。
2、調(diào)用所包含的Window對象的superDispatchTouchEvent()。
3、如果Window類沒有消耗該消息,則調(diào)用onTouchEvent()。
下面看看Window類中的superDispatchTouchEvent()。此時(shí)Window類的實(shí)現(xiàn)就是PhoneWindow類,該函數(shù)繼而調(diào)用mDecor的superDispatchTouchEvent(),而在DecorView的該函數(shù)中又調(diào)用super.dispatchTouchEvent,即ViewGroup的dispatchTouchEvent函數(shù)。注意這里的調(diào)用過程,一般的消耗處理流程是當(dāng)上一步?jīng)]有消耗消息時(shí)才執(zhí)行下一個(gè)處理邏輯,而在跟視圖DecorView中,則是當(dāng)沒有Callback時(shí)才調(diào)用ViewGroup的消息處理邏輯,而不是當(dāng)Callback沒有消耗消息時(shí)才調(diào)用ViewGroup的消息處理邏輯,原因就是Callback本身就會調(diào)用ViewGroup的消息處理邏輯。
ViewGroup的內(nèi)部消息派發(fā)過程
ViewGroup內(nèi)部的處理邏輯也采用遞歸方式,但與按鍵處理的遞歸有所不同。觸摸消息處理中首先會把消息派發(fā)給View樹中最后一個(gè)子視圖,如果子視圖沒有消耗該消息,才遞歸派發(fā)給其父視圖,而在按鍵消息處理時(shí),遞歸的過程正好相反。
1、將布局左邊轉(zhuǎn)化為視圖坐標(biāo)。
為什么要轉(zhuǎn)換呢?因?yàn)榻酉聛硪袛嘣撟鴺?biāo)落到了該ViewGroup中的哪個(gè)子視圖中,子視圖的位置都是相對于該ViewGroup的視圖坐標(biāo)的。
2、處理DOWN消息,其作用是判斷該視圖坐標(biāo)落到了哪個(gè)子視圖中。
(1)首先判斷ViewGroup本身是否被禁止獲取Touch消息,如果沒有禁止,并且回調(diào)函數(shù)onInterceptTouchEvent()中沒有消耗該消息,則意味著該消息可以傳遞給子視圖。如果子視圖消耗了該DOWN消息,則直接返回true。
(2)開始尋找子視圖。調(diào)用child.getHitRect(frame)函數(shù)獲取該子視圖在父視圖中的布局坐標(biāo),即該ViewGroup為該child分配的位置是什么,這個(gè)位置相對于該child來講是布局坐標(biāo),而相對于ViewGroup來講卻是視圖坐標(biāo),參數(shù)frame是執(zhí)行完畢后的位置輸出矩形。得到位置后,就可以調(diào)用frame.contain()方法判斷該消息位置是否被包含到了該child中,如果包含,并且該child也是一個(gè)ViewGroup,則準(zhǔn)備遞歸調(diào)用該child的dispatchTouchEvent(),在調(diào)用之前,首先需要把坐標(biāo)重新轉(zhuǎn)換到child的坐標(biāo)系中。
(3)完成了遞歸操作錢的坐標(biāo)轉(zhuǎn)換工作,接下來判斷該child是否是ViewGroup類。如果是就遞歸調(diào)用到ViewGroup的dispatchTouchEvent(),重新從第一步開始執(zhí)行,如果child不是ViewGroup,而是一個(gè)View,則意味著遞歸調(diào)用的結(jié)束。
3、如果是UP活著CANCEL消息,則消除mGroupFlags中的FLAG_DISALLOW_INTERCEPT標(biāo)識,即允許該ViewGroup截獲消息。
4、判斷target變量是否為空。空代表了所有的子窗口沒有消耗該消息,所以該ViewGroup本身需要處理該消息。在第二步中,如果匹配到某個(gè)child, 并且該child消耗了消息后,會將該child賦值給父視圖中的mMotionTarget變量。在該步中,首先要還原消息的原始位置,因?yàn)樵诘诙街?#xff0c;為了判斷子視圖是否包含該消息中的位置,對位置進(jìn)行了從布局坐標(biāo)到視圖坐標(biāo)的轉(zhuǎn)換,而此時(shí)則需要把視圖坐標(biāo)重新轉(zhuǎn)換為布局坐標(biāo),因?yàn)榻酉聛硪{(diào)用super.dispatchTouchEvent(),即View類的該函數(shù)。View類中處理該函數(shù)時(shí),需要布局坐標(biāo)。轉(zhuǎn)換之后,直接調(diào)用super.dispatchTouchEvent(),并返回其執(zhí)行結(jié)果,該函數(shù)內(nèi)部僅僅是回調(diào)onTouchEvent(),調(diào)用之前先判斷mPrivateFlags中是否包含CANCEL_NEXT_UP_EVENT標(biāo)識,該表示一般情況下不會存在,如果存在,則將消息的action類型改為ACTION_CANCEL。
5、處理target存在,并且變量disallowInercept為false;即允許截獲,在默認(rèn)況下ViewGroup都是允許截獲消息的,只有當(dāng)該ViewGroup的子視圖調(diào)用父視圖的requestDisallowInterceptTouchEvent()函數(shù)時(shí),方可禁止父視圖再次截獲消息。但每次UP消息或者CANCEL消息之后,該Viewgroup又會重新截獲信息。注意,在本步中,如果不允許截獲消息,那么也就不會調(diào)用onInterceptTouchEvent()函數(shù)了,如果允許,并且onTerceptTouchEvent()消耗了該消息,才執(zhí)行本步的操作。
6、在大多數(shù)情況下都會執(zhí)行到該步,即target存在,并且ViewGroup本身不允許截獲消息或者允許截獲但是卻沒有消耗消息,于是調(diào)用target.dispatchTouchEvent()把該消息繼續(xù)交給目標(biāo)視圖處理。在調(diào)用該函數(shù)前需要檢查target中是否聲明過要取消隨后的消息,即mPrivateFlags中包含CANCEL_NEXT_UP_EVENT,如果是,則把消息action值修改為CANCEL,置空mMotionTarget變量,因?yàn)閠arget不想處理接下來的消息,那么就可以認(rèn)為沒有target了。
以上就是Touch消息在ViewGroup內(nèi)部的遞歸和派發(fā),在這里注意區(qū)分onInterceptTouchEvent()和onTouchEvent()。
onInterceptTouchEvent()是在ViewGroup中定義的,即只有ViewGroup的子類能夠重載該方法。而onTouchEvent()函數(shù)有兩個(gè)定義,一個(gè)是在View類中定義的,所有View類的子類都可以重載該方法,包括ViewGroup,另一個(gè)是在Activity中定義的,用戶Activity可以重載該函數(shù)。View系統(tǒng)的消息處理機(jī)制中,會先執(zhí)行視圖內(nèi)部的onTouchEvent,如果沒有處理,才會調(diào)用Activity中的onTouchEvent()。
另外,對于ViewGroup而言,在一般情況下會先調(diào)用onInterceptTouchEvent(),只有當(dāng)該函數(shù)沒有消耗掉消息,并且其包含的子視圖也沒有消耗掉該消息時(shí),才會執(zhí)行該ViewGroup的onTouchEvent()。而對于View而言,沒有onInterceptTouchEvent()被調(diào)用。但并不是所有的消息處理過程都是先調(diào)用onInterceptTouchEvent(), 只有兩種情況才會調(diào)用到onInterceptTouchEvent()。
1.即在以上第2步驟中,當(dāng)時(shí)DOWN消息,并且ViewGroup允許截獲消息時(shí)。
2.即在以上第5步驟中,當(dāng)ViewGroup中存在target對象,并且允許截獲消息時(shí)。
View內(nèi)默認(rèn)消息派發(fā)過程
1、調(diào)用onFilterTouchEventForSecurity() 處理窗口處于模糊顯示狀態(tài)下的消息。所謂的模糊顯示是指,應(yīng)用程序可以設(shè)置當(dāng)前窗口為模糊狀態(tài),此時(shí)窗口內(nèi)部的所有視圖將顯示為模糊效果。這樣做的目的是為了隱藏窗口中的內(nèi)容,對于其中各個(gè)視圖而言,可以設(shè)置該視圖的FILTER_TOUCHES_WHEN_OBSCURED標(biāo)識,如果存在該標(biāo)識,則意味著用戶希望不要處理該消息。
2、回調(diào)視圖監(jiān)聽者的onTouch()函數(shù),如果監(jiān)聽者消耗了該消息,則直接返回。
3、調(diào)用onTouchEvent(),應(yīng)用程序可以重載該函數(shù),但如果沒有重載的話,該函數(shù)內(nèi)部有默認(rèn)的執(zhí)行方式,默認(rèn)的執(zhí)行流程如下:
(1)判斷該視圖是否為disable狀態(tài),如果是,什么都不做,返回true,即消耗該消息
(2)處理消息代理TouchDelegate。所謂的消息代理是指,可以給某個(gè)View指定一個(gè)消息處理代理,當(dāng)View收到消息時(shí),首先將該消息派發(fā)給其代理進(jìn)行處理。如果代理內(nèi)部消耗了該消息,則View不需要再進(jìn)行任何處理;如果代理沒有處理,則View繼續(xù)按照默認(rèn)的邏輯進(jìn)行處理。該類的目的是為了擴(kuò)大點(diǎn)擊區(qū)。(沒有實(shí)現(xiàn))
(3)判斷該視圖是否是可以點(diǎn)擊的,如果不可點(diǎn)擊,則直接返回false,即不處理該消息。否則,真正開始執(zhí)行觸摸消息的默認(rèn)處理邏輯,該邏輯中分別處理了ACTION_DOWN、MOVE和UP消息,
1??在ACTION_DOWN消息中,給mPrivateFlags變量添加PRESSED標(biāo)識,并將變量mHasPerformLongPress置為false,然后啟動tap監(jiān)測,即發(fā)送一個(gè)異步延遲消息,延遲時(shí)間為ViewConfigration.getTapTimeout()。
2??對于觸摸消息而言,消息本身沒有repeat的屬性,這與按鍵消息不同,一次觸摸消息只有一個(gè)DOWN消息,接下來就是連續(xù)的MOVE消息,并最終以UP消息結(jié)束。
3??處理ACTION_UP消息,代碼中判斷該UP消息是發(fā)生在哪一個(gè)檢測時(shí)間段中,并據(jù)此進(jìn)行不同的處理。
4、處理ACTION_CANCEL消息,這里只需要清除PRESSED標(biāo)識,并改變視圖狀態(tài),然后再關(guān)閉tap檢測即可。
至此Touch消息的一次處理過程就結(jié)束了。
總結(jié)
以上是生活随笔為你收集整理的android触摸消息的派发过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到已故长辈代表什么
- 下一篇: RecyclerView详细了解