一敷硅、概念
在界面中只要內(nèi)外兩層同時可以滑動,這個時候就會產(chǎn)生滑動沖突旁赊。
二桦踊、沖突場景
場景1:外部滑動方向和內(nèi)部滑動方向不一致
外部左右滑動、內(nèi)部上下滑動终畅;
外部上下滑動籍胯,內(nèi)部左右滑動。
例子:ViewPager + ListView
將ViewPager和Fragment配合使用組成頁面滑動效果离福,可以通過左右滑動來切換頁面杖狼,而每個頁面內(nèi)部往往又是一個ListView,可以上下滑動妖爷。本來這種情況是有滑動沖突的蝶涩,但是ViewPager內(nèi)部處理了這種滑動沖突,因此采用ViewPager時我們無須關(guān)注這個問題絮识。如果我們采用的不是ViewPager而是ScrollView等绿聘,那就必須手動處理滑動沖突了,否則會造成內(nèi)外兩層只能有一層能夠滑動次舌。
場景2:外部滑動方向和內(nèi)部滑動方向一致
外部左右滑動熄攘、內(nèi)部左右滑動;
外部上下滑動垃它,內(nèi)部上下滑動鲜屏。
場景3:場景1和場景2兩種情況的嵌套
例子:SlideMenu + ViewPager + ListView
外部有一個SlideMenu左右滑動效果烹看,內(nèi)部有一個ViewPager左右滑動效果,ViewPager的每一個頁面中又是一個ListView上下滑動效果洛史。雖然場景3看起來更復雜惯殊,但是它是幾個單一的滑動沖突的疊加,因此只需要分別處理內(nèi)層和中層也殖、中層和外層之間的滑動沖突即可土思。
三、處理規(guī)則
場景1:
根據(jù)滑動是水平滑動還是豎直滑動來判斷到底由誰來攔截事件忆嗜〖喝澹可以根據(jù)滑動的角度、距離差以及速度差來確定滑動方向捆毫。
例子中闪湾,當用戶左右滑動的時候,需要讓外部的View攔截點擊事件绩卤,當用戶上下滑動的時候途样,需要讓內(nèi)部View攔截點擊事件。
場景2:
在業(yè)務上找到突破點濒憋。比如業(yè)務上有規(guī)定:當處于某種狀態(tài)時需要外部View響應用戶的滑動何暇,而處于另外一種狀態(tài)時則需要內(nèi)部View來響應View的滑動。
場景3:
同場景2一樣凛驮,只能在業(yè)務上找到突破點裆站。
四、滑動沖突的解決方式
1.外部攔截法(推薦)
外部攔截法是指點擊事件先經(jīng)過父容器的攔截處理黔夭,如果父容器需要此事件就攔截宏胯,如果不需要此事件就不攔截,這樣就可以解決滑動沖突問題纠修,這種方法比較符合點擊事件的分發(fā)機制胳嘲。外部攔截法需要重寫父容器的onInterceptTouchEvent方法厂僧,在內(nèi)部做相應的攔截即可扣草。
偽代碼如下:
//重寫父容器的onInterceptTouchEvent方法
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int x = (int)event.getX();
int y = (int)event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN: //按下事件不攔截
intercepted = false;
break;
case MotionEvent.ACTION_MOVE: //滑動事件根據(jù)需要攔截
if(父容器需要當前點擊事件) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP: //松開事件不攔截
intercepted = false;
break;
default:
break;
}
return intercepted;
}
2.內(nèi)部攔截法
內(nèi)部攔截法是指父容器不攔截任何事件,所有的事件都傳遞給子元素颜屠,如果子元素需要此事件就直接消耗掉辰妙,否則就交給父容器進行處理,這種方法和事件分發(fā)機制不一致甫窟,需要配合ViewGroup#requestDisallowInterceptTouchEvent方法才能正常工作密浑。
偽代碼如下:
//重寫子元素的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
parent.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if(父容器需要當前點擊事件) {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
//重寫父容器的onInterceptTouchEvent方法
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
if(action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
五、實例
問題:
App使用ViewPager + TabLayout + Fragment框架粗井,其中一個fragment的內(nèi)容是只有一個自定義的webview控件尔破,進行加載H5頁面街图,H5頁面上有一個水平方向上的圖片輪播控件,當水平滑動該圖片輪播控件時會出現(xiàn)App切換Tab現(xiàn)象懒构,即外層ViewPager控件攔截了該滑動事件餐济,導致內(nèi)層H5頁面的圖片輪播控件無法在手動滑動的情況下正常切換圖片。
方案:
當手指接觸到H5頁面的圖片輪播控件區(qū)域時胆剧,由JS調(diào)用native的接口設置一個標志位為true絮姆,表示當前的觸摸事件需要交給webview來處理,在webview的事件處理方法onTouchEvent中秩霍,在該標志位為true的前提下篙悯,如果當前是ACTION_DOWN事件,則調(diào)用父容器的requestDisallowInterceptTouchEvent(true)方法不允許父容器攔截事件铃绒,如果當前是ACTION_UP或者ACTION_CANCEL事件鸽照,則調(diào)用父容器的requestDisallowInterceptTouchEvent(false)方法允許父容器攔截事件,并將該標志位設置為false颠悬。