今天來(lái)介紹下Android必備的知識(shí)點(diǎn)之一——滑動(dòng)沖突∶蹲ぃ《Android開發(fā)藝術(shù)探索》書中有做了詳細(xì)闡述快骗,為了方便各位看官,我會(huì)介紹如何處理滑動(dòng)沖突偎漫。
- 場(chǎng)景重現(xiàn)
- 解決思路
- 范例
場(chǎng)景重現(xiàn)
外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向不一致爷恳,這種場(chǎng)景常見如ViewPager和內(nèi)嵌ListView的Fragment組合。ViewPager內(nèi)部處理了這種滑動(dòng)沖突象踊,但如果外層用ScrollView來(lái)實(shí)現(xiàn)的話那么就需要我們自行處理這種滑動(dòng)沖突温亲。
外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向一致。這種場(chǎng)景常見如ListView內(nèi)部項(xiàng)嵌套左右滑動(dòng)的圖片瀏覽列表通危,與外部ViewPager滑動(dòng)沖突铸豁。
上述兩種場(chǎng)景的嵌套。這種場(chǎng)景常見如ViewPager和復(fù)雜的ListView菊碟,ListView內(nèi)部項(xiàng)嵌套左右滑動(dòng)的圖片瀏覽列表节芥,ListView上下滑動(dòng),外部ViewPager左右滑動(dòng)
解決思路
場(chǎng)景一:當(dāng)用戶左右滑動(dòng)時(shí)逆害,需要讓外部的View攔截點(diǎn)擊事件头镊,當(dāng)用戶上下滑動(dòng)時(shí),需要讓內(nèi)部View攔截點(diǎn)擊事件魄幕∠嗤В可以依據(jù)滑動(dòng)路徑與水平方向的夾角、水平方向與豎直防線上的距離差或者水平與豎直方向的速度差來(lái)做判斷纯陨。
場(chǎng)景二:這種就不能用場(chǎng)景一的思路來(lái)處理坛芽,可以依據(jù)業(yè)務(wù)上的規(guī)定,如內(nèi)部滑動(dòng)ListView在最頂層等等翼抠,當(dāng)用戶處于某種狀態(tài)時(shí)需要外部View響應(yīng)用戶的滑動(dòng)咙轩,而處于另外一種狀態(tài)時(shí)需要內(nèi)部View的滑動(dòng)。
場(chǎng)景三:滑動(dòng)規(guī)則也無(wú)法直接根據(jù)滑動(dòng)的角度阴颖、距離差以及速度差來(lái)做判斷活喊,同樣只能在業(yè)務(wù)上找到突破點(diǎn)。
接下來(lái)具體介紹幾種通用處理思路
外部攔截法
點(diǎn)擊事件都經(jīng)由父容器攔截處理量愧,需要重寫父容器的onInterceptTouchEvent方法钾菊,在方法內(nèi)部做相應(yīng)攔截即可。
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;//不攔截
if(!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted = true;
}
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if(Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
default:
break;
}
mLastXIntercept = x;//x軸攔截距離
mLastYIntercept = y;//y軸攔截距離
return interceped;
}
內(nèi)部攔截法
父容器不攔截任何事件偎肃,所有事件都傳遞給子元素煞烫,如果子元素需要此事件就直接消耗掉,否則交由父容器進(jìn)行處理累颂,需要配合requestDisallowInterceptTouchEvent方法才能正常工作滞详。
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(Math.abs(deltaX) > Math.abs(deltaY)) {
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;
}
}
范例
下面附上一個(gè)實(shí)例來(lái)介紹這兩種方法,實(shí)現(xiàn)一個(gè)類似于ViewPager中嵌套ListView的效果。為了制造沖突環(huán)境茵宪,我們需要重寫一個(gè)類似ViewPager的自定義控件HorizontalScrollViewEx(繼承ViewGroup最冰,需要重寫onMeasure和onLayout方法)。實(shí)際上就是根據(jù)這兩種方法來(lái)處理的稀火,HorizontalScrollViewEx1使用外部攔截法暖哨;HorizontalScrollViewEx2使用內(nèi)部攔截法。需要的同學(xué)可以自取代碼(截取Android開發(fā)藝術(shù)探索)凰狞。
https://github.com/singwhatiwanna/android-art-res/tree/master/Chapter_3