滑动冲突的处理

界面中的内外两层同时可以滑动,就会产生滑动冲突。

常见场景

  1. 外部滑动方向和内部滑动方向不一致;
  2. 外部滑动方向和内部滑动方向一致;
  3. 前面两种情况的嵌套;

滑动冲突处理规则

场景一,根据滑动的方向,来判断究竟是父控件进行拦截还是子控件进行拦截;
场景二,根据业务需求进行判断,什么时候父控件进行拦截,什么时候子控件进行拦截;
场景三,情况复杂,同样根据需求在业务上寻找突破点;

补充:可以根据横向滑动距离和纵向滑动距离的差值,来判断究竟是横向滑动还是纵向滑动

具体的解决方法

外部拦截法

所谓外部拦截法就是所有的点击事件都要经过父容器,如果父容器需要处理就进行拦截,如果不需要处理就不拦截,交给子控件进行处理,外部拦截法需要重写父容器的onInterceptTouchEvent方法,伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    private int mLastX = 0;
private int mLastY = 0;

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

int x = (int) ev.getX();
int y = (int) ev.getY();

boolean intercepted = false;

switch (ev.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;
default:
break;
}


mLastX = x;
mLastY = y;

return intercepted;
}

内部拦截法

所谓内部拦截法就是父容器不拦截任何的点击事件,所有的点击事件都交给子View进行处理,如果子View,需要拦截的话就消耗事件,不需要拦截的话就将事件交给父容器进行处理,内部拦截法需要配合requestDisallowInterceptTouchEvent方法进行处理。内部拦截法需要重写子View的dispatchTouchEvent方法和父容器的onInterceptTouchEvent方法进行处理。伪代码如下所示:

子View:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int mLastX = 0;
int mLastY = 0;

ViewGroup parent;

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

int x = (int) ev.getX();
int y = (int) ev.getY();

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

parent.requestDisallowInterceptTouchEvent(true);

break;
case MotionEvent.ACTION_MOVE:

int deltaX = x - mLastX;
int deltaY = y - mLastY;

if ("父容器需要处理事件") {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}

return super.dispatchTouchEvent(ev);
}

父容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

boolean intercepted = false;

if (ev.getAction() == MotionEvent.ACTION_DOWN) {
intercepted = false;
} else {
intercepted = true;
}

return intercepted;
}
}

父容器不拦截DOWN事件,默认拦截DOWN事件之外的其他事件,这样当子元素调用parent.requestDisallowInterceptTouchEvent(false)方法时,父元素才能继续拦截所需的事件。

参考资料:
《Android开发艺术探索》

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器