scrollview嵌套listview产生的滑动冲突(recyclerview)
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
 
                    
                
 
                
             浙公网安备 33010602011771号
浙公网安备 33010602011771号
问题一:非常感谢博主,解决了我的问题,不过你在 ScrollView 的 onInterceptTouchEvent 的末尾 return true 会导致 ScrollView 里面的其他 View 无法再接收到 Event,所以还是应该改成 return super.onInterceptTouchEvent(ev) ,再次感谢