Android滑動事件沖突的解決方法
滑動是Android中不可缺少的一部分,多個滑動必然會產生沖突,比如我們最常見的是ScrollView中嵌套了ListView,一般做法是計算出ListView的總高度,這樣就不用去滑動ListView了。又比如一個ViewPager嵌套Fragment,Fragment里面又有ListView,這原本是有滑動沖突的,但是ViewPager內部去幫我們解決了這種沖突。那如果我們要自己解決沖突又該怎么辦呢。
下面有兩種方式來解決:
外部攔截法
外部攔截法是指在有點擊事件時都要經過父容器,那么在父容器時如果需要攔截就攔截自己處理,不需要則傳遞給下一層進行處理,下面看個例子:
首先定義一個水平滑動的HorizontalScrollViewEx,看主要代碼
主要的攔截是需要重寫onInterceptTouchEvent
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//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) + 5) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
//不攔截,否則子元素無法收到
intercepted = false;
break;
}
//因為當ViewGroup中的子View可能消耗了down事件,在onTouchEvent無法獲取,
// 無法對mLastX賦初值,所以在這里賦值一次
mLastX = x;
mLastY = y;
mLastYIntercept = y;
mLastXIntercept = x;
return intercepted;
}
在down事件不需要攔截,返回false,否則的話子view無法收到事件,將全部會由父容器處理,這不是希望的;up事件也要返回false,否則最后子view收不到。
看看move事件,當水平滑動距離大于豎直距離時,代表水平滑動,返回true,由父類來進行處理,否則交由子view處理。這里move事件就是主要的攔截條件判斷,如果你遇到的不是水平和豎直的條件這么簡單,就可以在這里進行改變,比如,ScrollView嵌套了ListView,條件就變成,當ListView滑動到底部或頂部時,返回true,交由父類滑動處理,否則自身ListView滑動。
在onTouchEvent中主要是做的滑動切換的處理
@Override
public boolean onTouchEvent(MotionEvent event) {
mVelocityTracker.addMovement(event);
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (getScrollX() < 0) {
scrollTo(0, 0);
}
scrollBy(-deltaX, 0);
break;
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
mVelocityTracker.computeCurrentVelocity(1000);
float xVelocityTracker = mVelocityTracker.getXVelocity();
if (Math.abs(xVelocityTracker) > 50) {//速度大于50則滑動到下一個
mChildIndex = xVelocityTracker > 0 ? mChildIndex - 1 : mChildIndex + 1;
} else {
mChildIndex = (scrollX + mChildWith / 2) / mChildWith;
}
mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
int dx = mChildIndex * mChildWith - scrollX;
smoothScrollBy(dx, 0);
mVelocityTracker.clear();
break;
}
mLastY = y;
mLastX = x;
return true;
}
在這個嵌套一個普通的ListView,這樣就可以解決水平和豎直滑動沖突的問題了。
<com.example.lzy.customview.HorizontalScrollViewEx android:layout_width="match_parent" android:layout_height="200dp"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_bright" android:text="2" /> <Button android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_green_dark" android:text="3" /> </com.example.lzy.customview.HorizontalScrollViewEx>
其他的部分代碼如果需要可以下載源碼來看
內部攔截法
內部攔截法是父容器不攔截任何事件,所有事件都傳遞給子view,如果需要就直接消耗掉,不需要再傳給父容器處理
下面重寫一個ListView,只需要重寫一個dispatchTouchEvent方法就OK
public class ListViewEx extends ListView {
private static final String TAG = "lzy";
private int mLastX;
private int mLastY;
public ListViewEx(Context context) {
super(context);
}
public ListViewEx(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListViewEx(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//子View的所有父ViewGroup都會跳過onInterceptTouchEvent的回調
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (Math.abs(deltaX) > Math.abs(deltaY) + 5) {//水平滑動,使得父類可以執(zhí)行onInterceptTouchEvent
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(ev);
}
}
在down事件調用getParent().requestDisallowInterceptTouchEvent(true),這句代碼的意思是使這個view的父容器都會跳過onInterceptTouchEvent,在move中判斷如果是水平滑動就由父容器去處理,父容器只需要把之前的onInterceptTouchEvent改為下面那樣,其他不變。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mLastX = x;
mLastY = y;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
return true;
}
return false;
} else {
//如果是非down事件,說明子View并沒有攔截父類的onInterceptTouchEvent
//說明該事件交由父類處理,所以不需要再傳遞給子類,返回true
return true;
}
}
最終實現效果就是下面那樣,兩個是用兩種方式實現的,上面的圓圈是一個簡單的自定義View練習

下載地址:Android滑動事件沖突
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Android開發(fā)之activiti節(jié)點跳轉
這篇文章主要介紹了Android開發(fā)之activiti節(jié)點跳轉的相關資料,需要的朋友可以參考下2016-04-04
Android中如何指定SnackBar在屏幕的位置及小問題解決
這篇文章主要給大家介紹了關于Android中如何指定SnackBar在屏幕的位置,以及一個小問題解決的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-03-03
AndroidStudio升級4.1后啟動失敗Plugin問題解決
這篇文章主要介紹了AndroidStudio升級4.1后啟動失敗Plugin問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10

