最近在做高德地圖的時候,由於使用者的要求,不得不用ScrollVew巢狀MapView,雖然很官方要求不建議這樣做,但也迫於無奈… 魔高一尺,道高一丈.有什麼事是程式設計師不能解決的,如果有那就是解決兩次.
鑑於用到了觸控事件,於是就來總結了Android的觸控事件機制.
首先當使用者進行螢幕操作的時候,則有兩種情況
-
一是按鍵事件
-
二是觸控事件
按鍵事件分為長按和點選事件,過於簡單,這裡不再進行總結.
觸控事件
觸控事件的組成:
- 一個actionDown
- n個actionMove
- 一個actionUp
- 一個onClick
- 一個onLongClick
- 一個onScroll
Android元件
- 繼承 ViewGroupo比如LinearLayout,ScrollView,GridView,extends–>ViewGroup的View
- ViewGroup容器
- 繼承於 View不包含其他的View ,如TextView,Edittext,Butto等
下面介紹一個講的好的一個部落格地址:
MotionEvent
所有Touch事件都被封裝成了MotionEvent物件,包括Touch的位置、時間、歷史記錄以及第幾個手指(多指觸控)等。
Android 事件的處理的分類
-
分發(很多人也稱作為傳遞事件) : dispatchTouchEvent函式
-
消費: onTouchEvent函式和OnTouchListener函式
-
攔截:onInterceptTouchEvent函式
我們都知道Android的觸控事件都是從外層傳遞到內層:由最外層的Activity——>ViewGroup——> ViewGroupo——>…….View.
第一觸控事件傳遞的開始一定是Activity;
第二傳遞方式是通過隧道方式傳遞;
第三一直傳遞到一個最外層的View,也就是頂級View,由該View的這個方法來進行分發。
對於Activity來說:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean isFlag = super.dispatchTouchEvent(ev);
return isFlag;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean isFlag = super.onTouchEvent(event);
return isFlag;
}複製程式碼
對於ViewGroup來說:
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean isFlag =super.onTouchEvent(event);
return isFlag;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean isFlag =super.onInterceptTouchEvent(event);
return isFlag;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
boolean isFlag =super.dispatchTouchEvent(event);
return isFlag;
}複製程式碼
對於View來說和Activity一樣,只有消費和攔截
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean isFlag = super.dispatchTouchEvent(ev);
return isFlag;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean isFlag = super.onTouchEvent(event);
return isFlag;
}複製程式碼
www.open-open.com/lib/view/op…
android中的Touch事件都是從ACTION_DOWN開始的:
單手指操作:ACTION_DOWN—ACTION_MOVE—-ACTION_UP
多手指操作:ACTION_DOWN—ACTION_POINTER_DOWN—ACTION_MOVE–ACTION_POINTER_UP—ACTION_UP.
如果都不進行攔截,都不消費的基本流程圖:
下面我們就用案例進行分析:
activity—-》ViewGroup(MyLinearLayout)—》ViewGroup(MySubView)
MyLinearLayout的dispatchTouchEvent返回false。
列印結果:
- 過程及結果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的dispatchTouchEvent;
而該TouchEventFather 控制元件的 dispatchTouchEvent 返回 false,表示對獲取到的事件停止向下傳遞,同時也不對該事件進行消費;
由於 TouchEventFather 獲取的事件直接來自 TouchEventActivity ,則會將事件返回給 TouchEventActivity 的 onTouchEvent 進行消費;
最後直接由 TouchEventActivity 來響應手指移動和抬起事件。
MyLinearLayout的dispatchTouchEvent返回true。
列印結果:
- 過程及結果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;
而該TouchEventFather 控制元件的 dispatchTouchEvent 返回 true,表示分發事件到 TouchEventFather 控制元件並由該控制元件的 dispatchTouchEvent 進行消費;
又因為TouchEventActivity 不斷的分發事件到 TouchEventFather 控制元件的 dispatchTouchEvent,而 TouchEventFather 控制元件的 dispatchTouchEvent 也不斷的將獲取到的事件進行消費。
MyLinearLayout的onInterceptTouchEvent返回true。
列印結果:
過程及結果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;
而該TouchEventFather 控制元件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示對事件進行分發並向下傳遞給 TouchEventFather 控制元件的 onInterceptTouchEvent 方法;
而該方法返回 true 表示對所獲取到的事件進行攔截並將事件傳遞給 TouchEventFather 控制元件的 onTouchEvent 進行處理,TouchEventFather 控制元件的 onTouchEvent 返回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接將事件返回給上級控制元件;
由於 TouchEventFather 獲取的事件直接來自 TouchEventActivity,所以 TouchEventFather 控制元件的 onTouchEvent 會將事件以冒泡方式直接返回給 TouchEventActivity 的 onTouchEvent 進行消費;
後續的事件則會跳過 TouchEventFather 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。
MyLinearLayout的onInterceptTouchEvent返回false。
列印結果:
過程及結果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;
而該控制元件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示對事件進行分發並向下傳遞給 TouchEventFather 控制元件的 onInterceptTouchEvent 方法;
該方法返回 false 表示事件會被放行並傳遞到子控制元件 TouchEventChilds 的 dispatchTouchEvent 方法;
同樣 TouchEventChilds 的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示對事件進行分發並向下傳遞給 TouchEventChilds 控制元件的 onInterceptTouchEvent 方法;
而TouchEventChilds 的 onInterceptTouchEvent 方法返回 super.onInterceptTouchEvent(ev) ,預設會將事件傳遞給 TouchEventChilds 的 onTouchEvent 進行處理;
而TouchEventChilds 的 onTouchEvent 返回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接將事件返回給上級控制元件;
由於 TouchEventChilds 獲取的事件直接來自 TouchEventFather,所以 TouchEventChilds 控制元件的 onTouchEvent 會將事件以冒泡方式直接返回給 TouchEventFather 的 onTouchEvent 進行消費;
而 TouchEventFather 的 onTouchEvent 也返回了 super.onTouchEvent(ev),同樣 TouchEventFather 的 onTouchEvent 也會將事件返回給上級控制元件;
而 TouchEventFather 獲取的事件直接來自 TouchEventActivity,所以 TouchEventFather 控制元件的 onTouchEvent 會將事件以冒泡方式直接返回給 TouchEventActivity 的 onTouchEvent 進行消費;
後續的事件則會跳過 TouchEventFather 和 TouchEventChilds 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。
MyLinearLayout的onInterceptTouchEvent返回false。MySubView返回true
列印結果:
過程及結果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;
該控制元件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),事件會分發到 TouchEventFather 的 onInterceptTouchEvent,此方法返回 false 表示放行當先事件;
事件會被傳遞到子控制元件 TouchEventChilds 的 dispatchTouchEvent 方法,dispatchTouchEvent 返回 true 表示事件被分發到 TouchEventChilds ,並由 dispatchTouchEvent 方法消費;
後續的事件也會不斷的重複上面的邏輯最終被 TouchEventChilds 的 dispatchTouchEvent 消費。
其實如果看原始碼的話比較複雜,總結一下,基本的規則是:
-
down事件首先會傳遞到onInterceptTouchEvent()方法
-
如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return false,內部view將也會獲取到down,那麼後續的move, up等事件將繼續會先傳遞給該ViewGroup,之後一樣傳遞給最終的目標view的onTouchEvent()處理。
-
如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return true,內部view不會貨到down,後續的move, up等事件也不再傳遞給onInterceptTouchEvent(),當然,move,up也不會傳給view
-
如果最終需要處理事件的view的onTouchEvent()返回了false,那麼該事件將被傳遞至其上一層次的view的onTouchEvent()處理。
-
如果最終需要處理事件的view 的onTouchEvent()返回了true,那麼後續事件將可以繼續傳遞給該view的onTouchEvent()處理。
-
group的touch和這個機制沒關係,與上一級有關係。
來自androidstarjack部落格地址:
如果你覺得此文對您有所幫助,歡迎入群 QQ交流群 :232203809
微信公眾號:終端研發部
(歡迎關注學習和交流)