[圖片上傳失敗...(image-85aaf7-1630895208631)]
發(fā)展壯大離不開(kāi)廣大客戶(hù)長(zhǎng)期以來(lái)的信賴(lài)與支持,我們將始終秉承“誠(chéng)信為本、服務(wù)至上”的服務(wù)理念,堅(jiān)持“二合一”的優(yōu)良服務(wù)模式,真誠(chéng)服務(wù)每家企業(yè),認(rèn)真做好每個(gè)細(xì)節(jié),不斷完善自我,成就企業(yè),實(shí)現(xiàn)共贏。行業(yè)涉及木屋等,在成都網(wǎng)站建設(shè)、網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣、WAP手機(jī)網(wǎng)站、VI設(shè)計(jì)、軟件開(kāi)發(fā)等項(xiàng)目上具有豐富的設(shè)計(jì)經(jīng)驗(yàn)。
[圖片上傳失敗...(image-8c09b-1630895208631)]
[圖片上傳失敗...(image-25abb8-1630895208631)]
日常處理的部分為RootView下面的ViewGroup和View部分,那么上面的PhoneWindow、DecorView和RootView是做什么用的呢?RootView本身可以作為上下溝通的橋梁使用。
(設(shè)計(jì)模式-組合模式)
PhoneWindow是Window的實(shí)現(xiàn)類(lèi),Window是抽象類(lèi),DecorView是它的一個(gè)內(nèi)部類(lèi)。所以,PhoneWindow的大部分消息,都是PhoneWindow通過(guò)DecorView傳遞給下面的View的,同時(shí)下面的View傳遞消息也是通過(guò)DecorView回傳給PhoneWindow。
事件的傳遞過(guò)程中,主要有三種情況:事件分發(fā)(dispatchTouchEvent)、事件攔截(onInterceptTouchEvent)、事件消費(fèi)(onTouchEvent)。這三種情況均有一個(gè)boolean型的返回值來(lái)控制事件的傳遞流程。
為什么只有ViewGroup有事件攔截:因?yàn)锳ctivity作為事件分發(fā)的開(kāi)始,攔截了就只能自己處理了;而View作為事件分發(fā)的最末端,攔不攔截都需要它處理。中間階段,攔截可做一些處理。
事件傳遞的順序:
Activity - PhoneWindow - DecorView - ViewGroup - View - Activity
如果我們點(diǎn)擊View1,系統(tǒng)如何傳遞給View1呢,而不是下面的ViewGroupA或者RootView。很明顯,我們需要一種機(jī)制來(lái)執(zhí)行消息的分發(fā)。而消息分發(fā)的最小單位是View,ViewGroup是View的子類(lèi),Activity是根布局.
事件消費(fèi)與否與具體消費(fèi)無(wú)關(guān),僅由返回值決定,true表示消費(fèi),false表示不消費(fèi)。
事件分發(fā),總覺(jué)得不好理解,感覺(jué)非常麻煩,因?yàn)樗婕暗降臇|西實(shí)在太多了,到底怎么分發(fā)與以下因素都有關(guān):在哪個(gè)視圖層級(jí)(Activity、ViewGroup、View),什么事件類(lèi)型(DOWN、MOVE、UP、CANCEL),在哪個(gè)回調(diào)方法(dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent),回調(diào)方法返回不同的返回值(true、false)。這些因素都會(huì)影響事件的分發(fā),如果單純死記硬背,就算當(dāng)時(shí)背過(guò)了,過(guò)一段時(shí)間也就忘了,所以,真正理解事件分發(fā),才是搞懂事件分發(fā)的關(guān)鍵。要理解事件分發(fā),那我們就需要弄清楚,為什么需要事件分發(fā),它具體又是做了什么呢?
我們現(xiàn)在知道,事件是Activity-ViewGroup-View這樣層層傳遞的,每個(gè)層級(jí)都應(yīng)該有處理事件的能力,顯然,我們需要兩個(gè)方法,一個(gè)用來(lái)傳遞事件,一個(gè)用來(lái)處理時(shí)事件。我們分別定義三個(gè)類(lèi):MActivity,MViewGroup,MView 來(lái)分別模擬Activity,ViewGroup和View,一步一步實(shí)現(xiàn)事件分發(fā)。
這個(gè)需求很簡(jiǎn)單,我們只是需要最里層View處理事件,假如,不光最里層View可以處理事件,包裹它的ViewGroup也能處理事件,這應(yīng)該怎么辦呢?比如,ViewGroup是可以點(diǎn)擊的。
【透鏡系列】看穿 觸摸事件分發(fā)
工作當(dāng)中遇到一個(gè)需求:有兩個(gè)重疊并且全屏的Framelayout,交互邏輯是點(diǎn)擊上層的button使得上層消失,下層可見(jiàn);再點(diǎn)擊下層使得下層消失上層出現(xiàn)。但是發(fā)現(xiàn)當(dāng)點(diǎn)擊上層非button區(qū)域時(shí),下層也會(huì)響應(yīng)點(diǎn)擊事件,但是點(diǎn)擊button區(qū)域則不會(huì)。這意味著點(diǎn)擊事件穿過(guò)上層到達(dá)下層。對(duì)于安卓事件機(jī)制一知半解的我無(wú)從下手,上網(wǎng)搜了一下發(fā)現(xiàn)在上層FrameLayout下增加:
這樣的確可以保證上層點(diǎn)擊事件不會(huì)到達(dá)下層,可是這是為什么呢?最初步的猜想是當(dāng)上層FrameLayout的clickable屬性為true時(shí),點(diǎn)擊事件已經(jīng)被上層攔截了。這個(gè)和當(dāng)你點(diǎn)擊button時(shí)響應(yīng)onclick()時(shí)的原理是不是一樣?這就需要從源碼分析了。
分析源碼前先大致了解一下安卓事件分發(fā)機(jī)制。
綜合上述對(duì)安卓事件分發(fā)的源碼分析知道,當(dāng)在上層FrameLayout中將clickable設(shè)置為true時(shí),其實(shí)就是
當(dāng)點(diǎn)擊事件到達(dá)上層framelayout時(shí),在onTouchEvent中對(duì)事件進(jìn)行攔截,使得點(diǎn)擊事件不會(huì)繼續(xù)向下傳遞。當(dāng)然,除了這個(gè)方法以外,也可以重寫(xiě)FrameLayout的onInterceptTouchEvent,在將這個(gè)函數(shù)的返回值為true,也可以實(shí)現(xiàn)攔截事件的作用。
Android中對(duì)視圖的Touch事件進(jìn)行分發(fā)處理。
單手指操作:ACTION_DOWN - ACTION_MOVE - ACTION_UP
多手指操作:ACTION_DOWN - ACTION_POINTER_DOWN - ACTION_MOVE - ACTION_POINTER_UP - ACTION_UP.
(1) dispatchTouchEvent() :事件分發(fā)
(2) onInterceptTouchEvent() :事件攔截
(3) onTouchEvent() :事件處理
ViewGroup 的相關(guān)事件有三個(gè):onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。
View 的相關(guān)事件只有兩個(gè):dispatchTouchEvent、onTouchEvent。
先分析ViewGroup的處理流程:首先得有個(gè)結(jié)構(gòu)模型概念:ViewGroup和View組成了一棵樹(shù)形結(jié)構(gòu),最頂層為Activity的ViewGroup,下面有若干的ViewGroup節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)之下又有若干的ViewGroup節(jié)點(diǎn)或者View節(jié)點(diǎn),依次類(lèi)推。如圖:
點(diǎn)擊事件達(dá)到頂級(jí) View(一般是一個(gè) ViewGroup),會(huì)調(diào)用 ViewGroup 的 dispatchTouchEvent 方法,如果頂級(jí) ViewGroup 攔截事件即 onInterceptTouchEvent 返回 true,則事件由 ViewGroup 處理,這時(shí)如果 ViewGroup 的 mOnTouchListener 被設(shè)置,則 onTouch 會(huì)被調(diào)用,否則 onTouchEvent 會(huì)被調(diào)用。也就是說(shuō)如果都提供的話,onTouch 會(huì)屏蔽掉 onTouchEvent。在 onTouchEvent 中,如果設(shè)置了 mOnClickListenser,則 onClick 會(huì)被調(diào)用。如果頂級(jí) ViewGroup 不攔截事件,則事件會(huì)傳遞給它所在的點(diǎn)擊事件鏈上的子 View,這時(shí)子 View 的 dispatchTouchEvent 會(huì)被調(diào)用。如此循環(huán)。