安卓開發過程中滑動衝突的情形主要有三類:
① 父view與子view的滑動方向不同,如:父view左右滑動,子view上下滑動或相反;
這種情形是比較簡單的,只需要根據不同的滑動動作進行相應的攔截與處理即可。
② 父view與子view的滑動方向相同,即,父view左右,子view也左右,父view上下,子view也是上下;
這種情形需要根據具體情況來進行攔截處理,比如父View在出現子View滑動到邊緣的情況才進行
攔截處理或者其他情況。
③ 以上兩種情形,多個View的嵌套。
這種情形雖比較複雜,但根據上面兩種情形的處理法則分開進行處理即可。
滑動衝突的解決策略的理論基礎爲安卓的事件分發機制,不甚瞭解的朋友可以去先補補這方面的知識。
針對滑動衝突的解決策略有以下兩種:
一種是外部攔截法:即是當事件滿足滑動條件,通過父View的onInterceptTouchEvent方法對其進行
攔截,攔截之後將直接進入父View的onTouchEvent進行事件消費,不會再傳入下級view;
二種是內部攔截法:即是通過子View的dispatchTouchEvent方法接收到down事件,然後獲取父View的requestDisallowInterceptTouchEvent方法禁止其onInterceptTouchEvent攔截,當滿足父View滑動條件的
時候才允許。
第二種方法需要父View不攔截down事件,一般情況下也是不攔截down事件的,因爲攔截了down事件,
所有子元素點擊事件都會失效。建議採用第一種方法,易於理解,不容易出錯。
針對第一種方法,下面:
一個下拉刷新的SwipeRefreshLayout裏面嵌套了左右滑動、並且在pullToRefreshListView頭的viewPage
和上下滑動pullToRefreshListView(這裏解釋一下,pullToRefreshListView禁掉了它的下拉刷新功能,只用它
的上拉刷新),若不做解決事件衝突處理,就會出現只能父View下拉刷新,viewPager左右滑動無效,並且當
ListView往下滑動後不能再上滑,因爲上滑時候的下拉事件被父view攔截了,所以這裏有了兩種衝突情形。
首先先分析viewPage與父view SwipeRefreshLayout的滑動衝突,一個上下,一個左右,那好辦,只要重寫SwipeRefreshLayout的onInterceptTouchEvent對滑動事件進行事件判斷,如果是左右滑動的那麼返回false不
進行攔截,事件將直接傳到子View,即傳給了viewPager,實現其左右滑動的操作。然後說說pullToRefreshListView
與父view SwipeRefreshLayout的滑動衝突,因爲兩者都要上滑即手勢下拉的操作,所以產生了衝突。而這裏我
們只是想當pullToRefreshListView滑動到頂部才使能SwipeRefreshLayout的下拉刷新,所以思路就是當沒滑動到
頂部的時候SwipeRefreshLayout的onInterceptTouchEvent方法返回false 不進行攔截,把事件傳給他的子View pullToRefreshListView,從而實現其上滑操作,直到滑動到了頂部父View纔對事件進行攔截,如此便解決了
事件的衝突。
獲取子View pullToRefreshListView,並開啓滑動監聽,判斷是否處於頂部
<span style="font-size:14px;"> if(getChildAt(0).getId() == R.id.pullToRefreshListView){
((PullToRefreshListView)getChildAt(0)).getRefreshableView().setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(firstVisibleItem==0){
isFirst = true;
}else{
isFirst = false;
}
}
});
}</span>
重寫onInterceptTouchEvent進行條件判斷
<span style="font-size:14px;"> @Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPrevX = MotionEvent.obtain(event).getX();
break;
case MotionEvent.ACTION_MOVE:
final float eventX = event.getX();
float xDiff = Math.abs(eventX - mPrevX);
if (xDiff > mTouchSlop) {
return false;
}
if(!isFirst){
return false;
}
}
return super.onInterceptTouchEvent(event);
}</span>
爲了精準判斷滑動的方式,我們在自定義viewgroup裏面重寫onInterceptTouchEvent時,需要對滑動
的xy軸距離判斷大小,若豎直方向距離大則認爲是豎直滑動,否則認爲是水平滑動。
而第二種方法一般代碼如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if("滿足條件")
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return super.dispatchTouchEvent(ev);
}