要想解决滑动冲突就必须好好理解 Android 的事件分发机制。不了解 Android 事件分发机制的请先参考资料学习一下。
一般有 2 种方法
1 外部拦截法
这个非常简单,因为事件是从父 view 向子 view 进行分发的,所以我们可以重写父控件的 onInterceptTouchEvent, 如果父容器需要某个事件就拦截,如果不需要就不拦截交给子view处理。
伪代码如下
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = false;
int x = (int) event.getX();
int y = (int) event.getY(); switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
intercept = false;
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要这个事件) {
intercept = true;
} else {
intercept = false;
}
break;
case MotionEvent.ACTION_UP:
intercept = false;
break;
} mLastX = x;
mLastY = y; return intercept;
}
这里有一点需要注意,父容器不能拦截 ACTION_DOWN 事件,因为父容器一旦拦截了 ACTION_DOWN 事件那么后面所有的事件就都由它来处理了,子 view 就接收不到任何事件了。
2 内部拦截法
父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则交给父容器就行处理,需要配合 requestDisallowInterceptTouchEvent 来干预父容器对事件的拦截。
伪代码如下
一要重写子view的 dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY(); switch (event.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;
} mLastX = x;
mLastY = y; return super.dispatchTouchEvent(event); // 干预之后再扔给父容器
}
二要重写父容器的 onInterceptTouchEvent 让父容器默认拦截除 ACTION_DOWN 以外的所有事件
public boolean onInterceptTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
内部拦截法不太好理解,比较饶。
外部拦截法与事件分发机制流程一致,简单好理解,所以一般情况下都会优先选择外部拦截法。