產(chǎn)生疑問:對于一個按鈕,我按下并抬起時草戈,觸發(fā)了該按鈕的 onClick() 方法,可是我按下然后滑動時侍瑟,我卻可以使該按鈕的父View開始滑動唐片。這個情景一定不陌生丙猬,我們的ListView和GridView經(jīng)常有這種操作出現(xiàn)。那么我們的系統(tǒng)是如何做到的呢费韭?靠的就是觸摸事件分發(fā)機制
觸摸事件單元:
按下 (ACTION_DOWN).
移動 (ACTION_MOVE).
抬起 (ACTION_UP).
取消 *(ATCION_CANCEL).
以按下 (ACTION_DOWN) 開始茧球,(中間可能包含某些移動 (ACTION_MOVE) 事件)抬起 (ACTION_UP) 或取消 (ACTION_CANCEL) 結束的一系列觸摸事件的集合被稱為觸摸事件流。
例如 onClick() 事件:
按下(ACTION_DOWN), 加上 抬起 (ACTION_UP),這組事件流就構成了 onClick() 事件星持。
觸摸事件會傳入View的onTouchEvent() 方法中:
public class mView extends View{
...
@Override
public boolean onTouchEvent(MotionEvent event){
switch(event.getActionMasked()){
case MotionEvent.ACTION_UP:
// Do something
...
}
return true;
}
}
/*
event參數(shù)中包含了各種觸摸操作的信息:包括事件類型(是按下抢埋,抬起還是其他),坐標督暂,等等揪垄。
*/
函數(shù)返回類型為boolean,我們知道所有觸摸事件流都只能是從 按下 (ACTION_DOWN) 開始的
逻翁,這個方法從用戶觸摸的點開始由上而下依次向各級View詢問:你是否要消費這組事件饥努?哪個View的onTouchEvent() 方法先接受到 按下(ACTION_DOWN) 事件,并返回了true八回,哪個View就接管了這個事件流酷愧,后續(xù)的觸摸事件都將交給這個View執(zhí)行。
根據(jù)置頂圖舉個例子:用戶點擊了子View缠诅,那么系統(tǒng)開始從子View開始由上而下依次調用子View溶浴,父View,爺View的onTouchEvent()
方法滴铅,哪個View的該方法在接收到按下(ACTION_DOWN) 事件后返回了true戳葵,哪個View就開始處理這個按下(ACTION_DOWN) 事件的后續(xù)所有事件就乓,直到抬起(ACTION_UP) 事件或者取消(ACTION_CANCEL) 事件傳入汉匙。在這三個View中,如果沒有重寫它們的onTouchEvent() 方法生蚁,那么子View的onTouchEvent() 在率先接收到按下(ACTION_DOWN) 事件后會接管后續(xù)事件直到事件結束噩翠。如果子View在接收到按下(ACTION_DOWN) 事件時返回了false,那么系統(tǒng)會繼續(xù)向下去詢問父View邦投,你要不要接管這組事件伤锚。以此類推。
不瞞你說志衣,其實在進行從上往下調用onTouchEvent() 之前屯援,系統(tǒng)偷偷地從最底下的那個根View 依次向上逐級調用了onInterceptTouchEvent() 方法。產(chǎn)生任何觸摸事件時都會由下而上地調用各級View的onInterceptTouchEvent() 來詢問是否攔截事件念脯。該方法默認返回false狞洋,也就是不攔截,由于onInterceptTouchEvent() 會監(jiān)聽任何觸摸事件绿店,所以你可以在該方法內寫入自己的算法吉懊,并返回true庐橙,以達成某個View在滿足特定條件后立即接管該事件流后續(xù)事件的目的。在接管了該事件流后借嗽,新的接管者將把后續(xù)觸摸事件傳入自己的onTouchEvent()方法中處理态鳖,并且新的接管者將向前任接管者發(fā)送取消(ACTION_CANCEL) 事件(我接盤了!)來徹底停止前任接管者的處理中間狀態(tài)恶导。這個就叫做事件攔截機制浆竭。
public class mViewGroup extends ViewGroup{
@Override
public boolean onInterceptTouchEvent(MotionEvent event){
switch(event.getActionMasked()){
...
if(符合條件){
return true;
}
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event){
switch(event.getActionMasked()){
...
}
return true;
}
}
綜合之前的onTouchEvent() 方法,我們梳理一下這兩個方法之間的順序:當某一觸摸事件傳入系統(tǒng)時惨寿,系統(tǒng)先由下而上地執(zhí)行各級View的onInterceptTouchEvent() 方法兆蕉。再由上而下地執(zhí)行各級View的onTouchEvent() 方法。
沒錯腕唧,我又要不瞞你說了。還想說一個方法:requestDisallowInterceptTouchEvent()瘾英。這個方法是在子View 中調用的枣接,用來阻止其父View 的onInterceptTouchEvent() 方法生效。并且它會遞歸地阻止每一級父View 的攔截方法缺谴。例如我想在滑動屏幕時移動父View 但惶,長按我的子View后的滑動可以移動子View 本身而不是父View,那么我在子View 的onTouchEvent() 方法里適當?shù)牡胤綄懮?strong>requestDisallowInterceptTouchEvent() 就能實現(xiàn)特殊的需求湿蛔。
例如我可以在子View 的長按的case 下調用該方法膀曾,那么接下來的移動(ATCION_MOVE) 事件就不會交給父View 的onTouchEvent() 執(zhí)行了。
最后不瞞你說阳啥,感謝Hencoder項目添谊,感謝朱凱老師,本文是朱老師課后的學習心得總結苫纤。