一障本,事件分發(fā)
1,有哪些事件响鹃?
事件 | 簡介 |
---|---|
ACTION_DOWM | 手指初次接觸到屏幕時(shí)觸發(fā) |
ACTION_MOVE | 手指在屏幕上滑動時(shí)觸發(fā)驾霜,會多次觸發(fā) |
ACTION_UP | 手指離開屏幕時(shí)觸發(fā) |
ACTION_CANCEL | 事件被上層攔截時(shí)觸發(fā) |
2,view 繼承關(guān)系
view 子類 Imgview Textview 等
子類 ViewGroup : LinearLayout买置,RelativeLayout粪糙,ListView,Recyclerview等
繼承自View的只能處理事件忿项,繼承自ViewGroup的才能進(jìn)行分發(fā)事件(先分發(fā)在處理蓉冈,比View多了一個(gè)分發(fā)流程)
2.1,總流程
Activity#dispatchTouchEvent()-》PhineWindow#superDispatchTouchEvent()-》DecorView#superDispatchTouchEvent()
-》ViewGroup#dispatchTouchEvent--》...->View#dispatchTouchEvent ->#onTouchEvent
2.2轩触, View寞酿,ViewGroup 事件分發(fā),文字解說
- 2.2.1. ViewGroup 比View 多了一個(gè)事件onInterceptTouchEvent脱柱; ViewGroup 繼承 View 伐弹,dispatchTouchEvent、onTouchEvent
- 2.2.2.ViewGroup 和 View 組成了一個(gè)樹狀結(jié)構(gòu)榨为,根節(jié)點(diǎn)為 Activity 內(nèi)部包含的一個(gè)ViwGroup惨好。
- 2.2.3.觸摸事件由 Action_Down煌茴、Action_Move、Aciton_UP 組成日川,其中一次完整的觸摸事件中蔓腐,Down 和 Up 都只有一個(gè),Move 有若干個(gè)龄句,可以為 0 個(gè)合住。
- 2.2.4.當(dāng) Acitivty 接收到 Touch 事件時(shí),將遍歷子 View 進(jìn)行 Down 事件的分發(fā)撒璧。ViewGroup 的遍歷可以看成是遞歸的透葛。分發(fā)的目的是為了找到真正要處理本次完整觸摸事件的 View,這個(gè) View 會在 onTouchuEvent 結(jié)果返回 true卿樱。
- 2.2.5.當(dāng)某個(gè)子 View 返回 true 時(shí)僚害,會中止 Down 事件的分發(fā),同時(shí)在 ViewGroup 中記錄該子 View繁调。接下去的 Move 和 Up 事件將由該子 View 直接進(jìn)行處理萨蚕。由于子View是保存在ViewGroup中的,多層ViewGroup的節(jié)點(diǎn)結(jié)構(gòu)時(shí)蹄胰,上級ViewGroup保 存 的 會 是 真 實(shí) 處 理 事 件 的 View 所 在 的 ViewGroup 對 象 : 如ViewGroup0-ViewGroup1-TextView 的結(jié)構(gòu)中岳遥,TextView 返回了 true,它將被保存在 ViewGroup1 中裕寨,而 ViewGroup1 也會返回 true浩蓉,被保存在 ViewGroup0 中。當(dāng)Move 和 UP 事件來時(shí)宾袜,會先從 ViewGroup0 傳遞至 ViewGroup1捻艳,再由 ViewGroup1傳遞至 TextView。
- 2.2.6.當(dāng) ViewGroup 中所有子 View 都不捕獲 Down 事件時(shí)庆猫,將觸發(fā) ViewGroup 自身的 onTouch 事件认轨。觸發(fā)的方式是調(diào)用 super.dispatchTouchEvent 函數(shù),即父類 View的 dispatchTouchEvent 方法月培。在所有子 View 都不處理的情況下嘁字,觸發(fā) Acitivity 的onTouchEvent 方法。
- 2.2.7.onInterceptTouchEvent 有兩個(gè)作用:1.攔截 Down 事件的分發(fā)杉畜。2.中止 Up 和 Move事件向目標(biāo) View 傳遞纪蜒,使得目標(biāo) View 所在的 ViewGroup 捕獲 Up 和 Move 事件。
自我一句話總結(jié):ViewGroup 先通過dispatchTouchEvent進(jìn)行分發(fā),中途ViewGroup 有權(quán)不分發(fā)下去(通過onInterceptTouchEvent 返回return true 進(jìn)行攔截)寻行,不攔截情況下 在傳遞到View 的dispatchTouchEvent 霍掺,View若不進(jìn)行消費(fèi)(onTouchEvent )),轉(zhuǎn)給ViewGroup 的 onTouchEvent
可以理解:總經(jīng)理 -》部門經(jīng)理-》組長-》工人; 事件下去過程的分配杆烁,賺錢的項(xiàng)目上級的有權(quán)選擇自己做牙丽;若工人接到任務(wù),能力有限兔魂,同樣也可以交回上級處理烤芦;
疑問:對于上面舉例,要是好事情都被上級攔截析校;那工人豈不造反构罗;利益不均?智玻,一旦分發(fā)到子View遂唧,子View也有方法請求父容器不攔截;
于是可以看下:外部攔截法吊奢、內(nèi)部攔截法
3盖彭,View滑動沖突處理方法(外部攔截法、內(nèi)部攔截法)
引入https://blog.csdn.net/z_l_p/article/details/53488085页滚,思路肯定是對的召边,代碼完不完全對沒有測試過
1、外部攔截法 (子view代碼無需修改)(符合view事件分發(fā)機(jī)制)
說明:需要在父ViewGroup裹驰,重寫onInterceptTouchEvent方法隧熙,根據(jù)業(yè)務(wù)需要,判斷哪些事件是父Viewgroup需要的幻林,需要的話就對該事件進(jìn)行攔截贞盯,然后交由onTouchEvent方法處理,若不需要滋将,則不攔截邻悬,然后傳遞給子view或子viewGroup,
代碼:
public boolean onInterceptTouchEvent(MotionEvent ev) {
int y= (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
yDown=y;
isIntercept=false;
break;
case MotionEvent.ACTION_MOVE:
yMove=y;
if (yMove-yDown<0){ //根據(jù)業(yè)務(wù)需求更改判斷條件症昏,判斷是時(shí)候需要攔截
isIntercept=false;
}else if(yMove-yDown>0&&getChildAt(0).getScrollY()==0){
isIntercept=true;
}else if(yMove-yDown>0&&getChildAt(0).getScrollY()>0){
isIntercept=false;
}
break;
case MotionEvent.ACTION_UP:
isIntercept=false;
break;
}
return isIntercept; //返回true表示攔截随闽,返回false表示不攔截
}
2、內(nèi)部攔截法(父viewgroup需要重寫onInterceptTouchEvent)(不符合view事件分發(fā)機(jī)制)
個(gè)人理解就是肝谭,攔截前 先判斷子View時(shí)候是否改變過disallowIntercept值 disallowIntercept是true根本不執(zhí)行攔截方法了
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
說明:顧名思義就是在子view中攔截事件掘宪,父viewGroup默認(rèn)是不攔截任何事件的,所以攘烛,當(dāng)事件傳遞到子view時(shí)魏滚,
子view根據(jù)自己的實(shí)際情況來,如果該事件是需要子view來處理的坟漱,那么子view就自己消耗處理鼠次,如果該事件不需要由子view來處理,那么就調(diào)用getParent().requestDisallowInterceptTouchEvent()方法來通知父viewgroup來攔截
這個(gè)事件,也就是說腥寇,叫父容器來處理這個(gè)事件成翩,這剛好和view的分發(fā)機(jī)制相反。
代碼: (需要注意赦役,要確保MotionEvent.ACTION_DOWN時(shí)不攔截)
//子view的代碼·
public boolean dispatchTouchEvent(MotionEvent ev) {
int y= (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
yDown=y;
break;
case MotionEvent.ACTION_MOVE:
yMove=y;
Log.e("mes",yMove+"B榈小!掂摔!");
int scrollY = getScrollY();
if (scrollY == 0&&yMove-yDown>0) { //根據(jù)業(yè)務(wù)需求判斷是否需要通知父viewgroup來攔截處理該事件
//允許父View進(jìn)行事件攔截
Log.e("mes",yMove-yDown+"攔截");
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(ev);
}
//父viewgroup代碼 (要確保down是不攔截术羔,move和up時(shí)要攔截)
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_DOWN){
return false;
}else{
return true;
}
}