view-滑动冲突

    一篇记录了关于View事件的传递,这篇将记录一下关于View的滑动冲突问题。《Android开发艺术探索》书中非常好的总结了事件滑动冲突的场景以及解决办法,这里只是做一个笔记。

1、常见滑动冲突场景

  • 场景一:外部滑动方法和内部滑动方向不一致;
  • 场景二:外部滑动方向和内部滑动方向一致;
  • 场景三:上面两种情况的嵌套。

2、处理规则

当场景一,用户左右滑动,让外部的view拦截点击事件,用户上下滑动,让内部view拦截点击事件。只要有滑动,根据滑动的起始点与终止点坐标位置,判断垂直滑动距离大还是水平滑动距离大,做出相应的拦截处理。其他场景也是类似,根据情况做出处理。

3、解决办法

外部拦截
所有的点击事件都必须先由父View拦截处理,父View需要拦截就拦截,不拦截就由子View处理。伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted=false;
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
intercepted=false;
break;
case MotionEvent.ACTION_MOVE:
if (满足父容器拦截要求){
intercepted=true;
}else {
intercepted=false;
}
break;
case MotionEvent.ACTION_UP:
intercepted=false;
break;
}
return intercepted;
}

注: ACTION_DOWN这个事件是不能拦截的,因为一旦拦截后续的事件都会由父容器处理了。
内部拦截
如果父View不拦截任何事件,所有事件都传给子View。如果子View需要处理此事件就直接消耗,否则就交给父View进行处理。父View完成这个功能需要配合requestDisallowInterceptTouchEvent()方法才可。这个方法表示是否让父容器拦截事件。伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (满足父容器拦截要求){
requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:

break;
}
return super.onInterceptTouchEvent(event);
}

注:父View默认拦截除了ACTION_DOWN以外的其他事件,这样子当元素调用parent.requestDisallowInterceptTouchEvent(false)时,父元素才能拦截所需的事件。

4、总结

解决滑动冲突有两种,外部拦截和内部拦截,推荐使用外部拦截,实现起来简单。实际中根据业务具体问题制定处理规则,选择适当的解决办法。