參考:https://blog.csdn.net/morgan_xww/article/details/9372285
https://www.jianshu.com/p/35a8309b9597
基礎知識
佈局可定義應用中的界面結構(例如 Activity 的界面結構)。佈局中的所有元素均使用 View 和 ViewGroup 對象的層次結構進行構建。View 通常繪製用戶可查看並進行交互的內容。然而,ViewGroup 是不可見容器,用於定義 View 和其他 ViewGroup 對象的佈局結構,如圖 1 所示。
View 對象通常稱爲“微件”,可以是衆多子類之一,例如 Button 或 TextView。
ViewGroup 對象通常稱爲“佈局”,可以是提供其他佈局結構的衆多類型之一,例如 LinearLayout 或 ConstraintLayout。
touch事件3個方法:
public boolean dispatchTouchEvent(MotionEvent ev); //用來分派event;從ViewGroup往view派發
public boolean onInterceptTouchEvent(MotionEvent ev); //用來攔截event;返回true表示攔截
public boolean onTouchEvent(MotionEvent ev); //用來處理event;處理觸控
Activity類 | Activity | dispatchTouchEvent(); onTouchEvent(); |
ViewGroup子類 | FrameLayout、LinearLayout、ListView等 | dispatchTouchEvent(); onInterceptTouchEvent(); onTouchEvent(); |
View控件 | Button、TextView、EditText…… | dispatchTouchEvent(); onTouchEvent(); |
dispatchTouchEvent() | 用來分派事件。 其中調用了onInterceptTouchEvent()和onTouchEvent(),一般不重寫該方法 |
onInterceptTouchEvent() | 用來攔截事件。 ViewGroup類中的源碼實現就是{return false;}表示不攔截該事件, 事件將向下傳遞(傳遞給其子View); 若手動重寫該方法,使其返回true則表示攔截,事件將終止向下傳遞, 事件由當前ViewGroup類來處理,就是調用該類的onTouchEvent()方法 |
onTouchEvent() | 用來處理事件。 返回true則表示該View能處理該事件,事件將終止向上傳遞(傳遞給其父View); 返回false表示不能處理,則把事件傳遞給其父View的onTouchEvent()方法來處理 |
此處參考:https://blog.csdn.net/morgan_xww/article/details/9372285
重點掌握
touch消息種類(基礎版)
ACTION_DOWN->按下
ACTION_MOVE->移動
ACTION_UP->鬆開
dispatchTouchEvent-事件分發順序
dispatchTouchEvent比較複雜,可以按照下面這張圖分析:ViewGroup和View組成了一棵樹形結構,最頂層爲Activity的ViewGroup,下面有若干的ViewGroup節點,每個節點之下又有若干的ViewGroup節點或者View節點,依次類推。
當一個Touch事件(ACTION_DOWN)依次下發,下發的過程是調用子View(ViewGroup)的dispatchTouchEvent方法實現的。簡單來說,就是ViewGroup遍歷它包含着的子View,調用每個View的dispatchTouchEvent方法,而當子View爲ViewGroup時,又會通過調用ViewGroup的dispatchTouchEvent方法繼續調用其內部的View的dispatchTouchEvent方法。上述例子中的消息action_down下發順序是這樣的:①-②-⑤-⑥-⑦-③-④。
dispatchTouchEvent方法只負責事件的分發,它擁有boolean類型的返回值,當返回爲true時,(ACTION_MOVE與ACTION_UP)順序下發會中斷。在上述例子中如果⑤的dispatchTouchEvent返回結果爲true,那麼⑥-⑦-③-④將都接收不到本次Touch事件但只收到了(ACTION_DOWN)。
ViewGroup的dispatchTouchEvent是真正在執行“分發”工作,而View的dispatchTouchEvent方法,並不執行分發工作,或者說它分發的對象就是自己,決定是否把touch事件交給自己處理,而處理的方法,便是onTouchEvent事件。
onTouchEvent-事件處理
- 如果返回值是true,表示消費(consume)了這個事件。以ACTION_DOWN爲例,如果某個控件的onTouchEvent返回值爲true,則後續的n個ACTION_MOVE與1個ACTION_UP都會逐層(通過ViewGroup,不會再遍歷View)傳遞到這個控件的onTouchEvent進行處理。
- 如果返回值是false,則會將ACTION_DOWN傳遞給其父ViewGroup的onTouchEvent進行處理,直到由哪一層ViewGroup消費了ACTION_DOWN事件爲止。
- 由於觸摸事件都是連續的。如果ACTION_MOVE傳遞到子控件,而子控件的onTouchEvent返回值是false,即沒有處理該ACTION_MOVE事件,則後續的ACTION_UP就不會傳到該子控件來了。
- 這裏要注意是逐層,也就是說每層(指的只是ViewGroup)的攔截器還是可以攔截到後續的ACTION_MOVE與ACTION_UP。如果後續的ACTION_MOVE與ACTION_UP被某層的攔截器攔截,則後續的事件將不會再傳遞給之前處理onTouchEvent的子控件,而是逐層傳遞給由攔截消息的這個控件的onTouchEvent函數進行處理,並且會向其之前接收事件的子控件發送一個ACTION_CANCEL,表示後續事件被取消了。
參考:https://www.jianshu.com/p/35a8309b9597
舉例子
佈局結構和ontouch事件的傳遞順序
長這樣:
注:XML的繪製順序是從外層到內層,同一層從上到下,繪製遲的覆蓋繪製早的;在這裏的XML,無論是否把MyButton寫在TextView後面,由於MyButton顯示優先級高於TextView所以顯示在TextView上,並且獲取事件優先級也高於TextView。
1和2點的邏輯示意圖
1.點白色部分FrameLayout:Frame的OntouchEvent返回false表示不消費事件;ACTION_UP就不傳給他了
引用圖片:https://blog.csdn.net/morgan_xww/article/details/9372285
2.點藍色部分RelativeLayout:RelativeLayout的OntouchEvent返回false表示不消費事件;ACTION_UP不傳給他了
由於觸摸事件都是連續的。如果ACTION_DOWN傳遞到子控件,而子控件的onTouchEvent返回值是false,即沒有處理該ACTION_DOWN事件,則後續的ACTION_MOVE和ACTION_UP就不會傳到該子控件來了。ACTION_DOWN事件一直傳遞到了RelativeLayout,但是最終是被MainActivity的onTouchEvent處理的,而從而導致ACTION_MOVE和ACTION_UP只傳遞到了MainActivity,最終也是由MainActivity處理的情況。
引用圖片:https://blog.csdn.net/morgan_xww/article/details/9372285
3.點淡綠色部分MyTextView:MyTextView返回true;ACTION_UP繼續傳給他
邏輯示意
引用圖片:https://blog.csdn.net/morgan_xww/article/details/9372285
4.點淡藍色部分MyButton:MyButton返回false;MyTextView返回true; ACTION_UP不再通過MyButton
由於觸摸事件都是連續的。如果ACTION_DOWN傳遞到子控件,而子控件的onTouchEvent返回值是false,即沒有處理該ACTION_DOWN事件,則後續的ACTION_MOVE和ACTION_UP就不會傳到該子控件來了。ACTION_DOWN事件一直傳遞到了MyButton,但是最終是被MyTextView的onTouchEvent處理的,而從而導致ACTION_MOVE和ACTION_UP只傳遞到了MyTextView,最終也是由MyTextView處理而跳過MyButton的情況。
參考這個