Android滑動沖突主要有兩種方法:
1、外部攔截法
- public class ListScrollView extends ScrollView {
- private ListView listView;
- public ListScrollView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- public ListScrollView(Context context) {
- super(context);
- }
- /**
- * 覆寫onInterceptTouchEvent方法,點擊操作發生在ListView的區域的時候,
- * 返回false讓ScrollView的onTouchEvent接收不到MotionEvent,而是把Event傳到下一級的控件中
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // TODO Auto-generated method stub
- if (listView != null && checkArea(listView, ev)) {
- return false;
- }
- return super.onInterceptTouchEvent(ev);
- }
- /**
- * 測試view是否在點擊范圍內
- * @param locate
- * @param v
- * @return
- */
- private boolean checkArea(View v, MotionEvent event){
- float x = event.getRawX();
- float y = event.getRawY();
- int[] locate = new int[2];
- v.getLocationOnScreen(locate);
- int l = locate[0];
- int r = l + v.getWidth();
- int t = locate[1];
- int b = t + v.getHeight();
- if (l < x && x < r && t < y && y < b) {
- return true;
- }
- return false;
- }
- public ListView getListView() {
- return listView;
- }
- public void setListView(ListView listView) {
- this.listView = listView;
- }
- }
2、內部攔截法
-
listView.setOnTouchListener( new OnTouchListener() {
-
-
public boolean onTouch(View v, MotionEvent event) {
-
// TODO Auto-generated method stub
-
listView.getParent().requestDisallowInterceptTouchEvent( true);
-
return false;
-
}
-
});
同時要保證父布局不能攔截actiondown事件,否則所有事件都會交給父布局,即父布局重寫onInterceptTouchEvent為:
-
-
public boolean onInterceptTouchEvent(MotionEvent e) {
-
int action = e.getAction();
-
if(action==MotionEvent.ACTION_DOWN) {
-
return false;
-
}
-
return super.onInterceptTouchEvent(e);
-
}
參考Android開發藝術探索
徹底解決問題(完美版)
外層ScrollView,內嵌ListView,都是垂直方向。采用內部攔截法,實現ListView能滾動時則讓ListView處理,當ListView滑到頂部或者底部不能滑動時讓ScrollView處理

布局 上面有一段文本,中間是ListView,下面還有一段文本
-
-
<org.icegeneral.scroll.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-
android:id="@+id/sv"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent">
-
-
<LinearLayout
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:orientation="vertical">
-
-
<TextView
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:text="AAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA" />
-
-
<org.icegeneral.scroll.MyListView
-
android:id="@+id/lv"
-
android:layout_width="match_parent"
-
android:layout_height="500dp"
-
android:background="#888888">
-
-
</org.icegeneral.scroll.MyListView>
-
-
<TextView
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:text="BBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB" />
-
</LinearLayout>
-
</org.icegeneral.scroll.MyScrollView>
MyScrollView
ACTION_DOWN必須讓給ListView,ListView才能收到ACTION_MOVE,ListView才能判斷自己是否還能滾動
第二點要注意的是:因為ACTION_DOWN讓給ListView,那么ACTION_DOWN就無法進入ScrollView的onTouchEvent, 但是ScrollView的滾動需要在ACTION_DOWN階段做一些准備,所以主動調用了一次
-
public class MyScrollView extends ScrollView {
-
-
public MyScrollView(Context context, AttributeSet attrs) {
-
super(context, attrs);
-
}
-
-
-
public boolean onInterceptTouchEvent(MotionEvent ev) {
-
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-
onTouchEvent(ev);
-
return false;
-
}
-
return true;
-
}
-
-
}
MyListView
-
public class MyListView extends ListView {
-
-
public MyListView(Context context, AttributeSet attrs) {
-
super(context, attrs);
-
}
-
-
private float lastY;
-
-
-
-
public boolean dispatchTouchEvent(MotionEvent ev) {
-
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-
getParent().getParent().requestDisallowInterceptTouchEvent( true);
-
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
-
if (lastY > ev.getY()) {
-
// 如果是向上滑動,且不能滑動了,則讓ScrollView處理
-
if (!canScrollList(1)) {
-
getParent().getParent().requestDisallowInterceptTouchEvent( false);
-
return false;
-
}
-
} else if (ev.getY() > lastY) {
-
// 如果是向下滑動,且不能滑動了,則讓ScrollView處理
-
if (!canScrollList(-1)) {
-
getParent().getParent().requestDisallowInterceptTouchEvent( false);
-
return false;
-
}
-
}
-
}
-
lastY = ev.getY();
-
return super.dispatchTouchEvent(ev);
-
}
-
-
}
Activity
-
protected void onCreate(Bundle savedInstanceState) {
-
....
-
scrollView.smoothScrollTo( 0, 0);
-
}
ACTION_DOWN的特殊性
解釋下為什么ListView的ACTION_UP無需調用
getParent().getParent().requestDisallowInterceptTouchEvent(false)
來看下ViewGroup源碼
-
-
public boolean dispatchTouchEvent(MotionEvent ev) {
-
...
-
boolean handled = false;
-
if (onFilterTouchEventForSecurity(ev)) {
-
final int action = ev.getAction();
-
final int actionMasked = action & MotionEvent.ACTION_MASK;
-
-
if (actionMasked == MotionEvent.ACTION_DOWN) {
-
cancelAndClearTouchTargets(ev);
-
resetTouchState(); //重點是這句
-
}
-
-
// Check for interception.
-
final boolean intercepted;
-
if (actionMasked == MotionEvent.ACTION_DOWN
-
|| mFirstTouchTarget != null) {
-
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
-
if (!disallowIntercept) {
-
intercepted = onInterceptTouchEvent(ev);
-
ev.setAction(action); // restore action in case it was changed
-
} else {
-
intercepted = false;
-
}
-
} else {
-
intercepted = true;
-
}
-
...
-
}
-
...
-
}
-
-
private void resetTouchState() {
-
clearTouchTargets();
-
resetCancelNextUpFlag( this);
-
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; //移除FLAG_DISALLOW_INTERCEPT
-
mNestedScrollAxes = SCROLL_AXIS_NONE;
-
}
所以如果是ACTION_DOWN,會調用resetTouchState(),移除FLAG_DISALLOW_INTERCEPT,所以ACTION_DOWN時,parent一定會進入onInterceptTouchEvent
其他
其實現在都用RecyclerView代替ListView,ScrollView嵌套RecyclerView時,對於手勢已經支持得很好,不必自己處理沖突。這個在demo里也有寫,做個對比
作者:風風風箏
鏈接:http://www.jianshu.com/p/2b038cd9ac14
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
問題二:博主你好,如果你不在up里面去將request設置成false的話,scrollview上的點擊事件就不能響應了
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
http://blog.csdn.net/colinandroid/article/details/72770863
http://www.jianshu.com/p/2b038cd9ac14
http://www.cnblogs.com/1426837364qqcom/p/5388902.html
http://blog.csdn.net/lys701/article/details/8755373
http://blog.csdn.net/chaihuasong/article/details/17499799(有關requestDisallowInterceptTouchEvent)
http://blog.csdn.net/Wisimer/article/details/49426653
問題一:非常感謝博主,解決了我的問題,不過你在 ScrollView 的 onInterceptTouchEvent 的末尾 return true 會導致 ScrollView 里面的其他 View 無法再接收到 Event,所以還是應該改成 return super.onInterceptTouchEvent(ev) ,再次感謝