一丶? 一個完整的事件傳遞機制? 必定包含三種事件: 由一個 down 事件、 多 個 move 事件,一個 up 事件組成(還有一個cancel事件,該事件是在被上層攔截時觸發(fā))。
手指落下(ACTION_DOWN) -> 移動(ACTION_MOVE) -> 離開(ACTION_UP) ?
二丶Touch一般的傳遞流程:Activity------>window(唯一實現(xiàn)類是PhoneWindow)------>頂級View(DecorView)------>ViewGroup------>View? ? ;(可概括為一句話:責任鏈模式,事件層層傳遞,直到被消費)
三丶監(jiān)聽Touch事件的兩種方式:setTouchListener和直接重寫三個方法:dispatchToucEvent,?onInterceptTouchEvent, ?onTouchEvent.
1.setTouchListener:
此方法監(jiān)聽的優(yōu)先級比較高,如果在onTouchListener的onTouch方法里面執(zhí)行了return true,那么說明消費了該事件 ,而OnTouchEvent是接收不到該事件的,因此在onClickListener里面的Onclick方法是執(zhí)行不到的,因為Onclick是在OnTouchEvent種被調用的,因此Touch事件走不到OnTouchEvent事件的話,Onclick方法是不會被執(zhí)行的.
2,dispatchToucEvent:
為什么ViewGroup有dispatchToucEvent,而View 也有dispatchToucEvent,是因為View 可以注冊很多事件監(jiān)聽器,例如:單擊事件(onClick)、長按事件(onLongClick)、觸摸事件(onTouch),并且View自身也有 onTouchEvent 方法,那么問題來了,這么多與事件相關的方法應該由誰管理?就是dispatchTouchEvent.()
那么,這么多的事件,他們的事件調度順序是怎樣的呢?
從源碼的角度去分析:?
從設計的角度去分析:
1).單擊事件(onClickListener) 需要兩個兩個事件(ACTION_DOWN 和 ACTION_UP )才能觸發(fā),如果先分配給onClick判斷,等它判斷完,用戶手指已經(jīng)離開屏幕了,定然造成 View 無法響應其他事件,應該最后調用。(最后)
2).長按事件(onLongClickListener) 同理,也是需要長時間等待才能出結果,肯定不能排到前面,但因為不需要ACTION_UP,應該排在 onClick 前面。(onLongClickListener > onClickListener)
3).觸摸事件(onTouchListener) 如果用戶注冊了觸摸事件,說明用戶要自己處理觸摸事件了,這個應該排在最前面。(最前)
4).View自身處理(onTouchEvent) 提供了一種默認的處理方式,如果用戶已經(jīng)處理好了,也就不需要了,所以應該排在 onTouchListener 后面。(onTouchListener > onTouchEvent)
由上可以推導出來:
事件的調度順序應該是onTouchListener > onTouchEvent > onLongClickListener > onClickListener。
舉個栗子:
在LinearLayout這里設置了點擊一個事件,然后在執(zhí)行的時候你會發(fā)現(xiàn)怎么點擊都不會接收到消息,這是因為View這里設置了clickble的屬性為true.也就是這個事件被孩子吃掉了.那么父親是不會再走TouchEvent的
3.onInterceptTouchEvent:
這個是ViewGroup專有的,該事件在ViewGroup一層一層傳遞的,最終傳遞給 View,ViewGroup 要比它的 ChildView 先拿到事件,并且有權決定是否告訴要告訴 ChildView。
一般情況下,該事件在ViewGroup中是這樣分發(fā)的:
1.判斷自身是否需要(詢問 onInterceptTouchEvent 是否攔截),如果需要,調用自己的 onTouchEvent。
2.自身不需要或者不確定,則詢問 ChildView ,一般來說是調用手指觸摸位置的 ChildView。
3.如果子 ChildView 不需要則調用自身的 onTouchEvent。
ViewGroup通過遍歷ChildView,確定手指點在哪個ChildView的區(qū)域內,然后將事件發(fā)放到該ChildView,而當ChildView存在覆蓋的情況時,ViewGroup會將事件分發(fā)到最上層的ChildView上(一般后加載的CHildView是會覆蓋前面加載了的,所以最上層的是最后加載的)
當手指點擊有重疊區(qū)域時,分如下幾種情況:
只有 View1 可點擊時,事件將會分配給 View1,即使被 View2 遮擋,這一部分仍是 View1 的可點擊區(qū)域。
只有 View2 可點擊時,事件將會分配給 View2。
View1 和 View2 均可點擊時,事件會分配給后加載的 View2,View2 將事件消費掉,View1接收不到事件。
注意:
上面說的是可點擊,可點擊包括很多種情況,只要你給View注冊了onClickListener、onLongClickListener、OnContextClickListener其中的任何一個監(jiān)聽器或者設置了android:clickable=”true”就代表這個 View 是可點擊的。
另外,某些 View 默認就是可點擊的,例如,Button,CheckBox 等。
給 View 注冊 OnTouchListener 不會影響 View 的可點擊狀態(tài)。即使給 View 注冊 OnTouchListener ,只要不返回 true 就不會消費事件。
3. ViewGroup 和 ChildView 同時注冊了事件監(jiān)聽器(onClick等),哪個會執(zhí)行?
事件優(yōu)先給 ChildView,會被 ChildView消費掉,ViewGroup 不會響應。
4. 所有事件都應該被同一 View 消費
在上面的例子中我們分析后可以了解到,同一次點擊事件只能被一個 View 消費,主要是為了防止事件響應混亂,如果再一次完整的事件中分別將不同的事件分配給了不同的 View 容易造成事件響應混亂。
View 中 onClick 事件需要同時接收到 ACTION_DOWN 和 ACTION_UP 才能觸發(fā),如果分配給了不同的 View,那么 onClick 將無法被正確觸發(fā)。
安卓為了保證所有的事件都是被一個 View 消費的,對第一次的事件( ACTION_DOWN )進行了特殊判斷,View 只有消費了 ACTION_DOWN 事件,才能接收到后續(xù)的事件(可點擊控件會默認消費所有事件),并且會將后續(xù)所有事件傳遞過來,不會再傳遞給其他 View,除非上層 View 進行了攔截。