要想解决滑动冲突就必须好好理解 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; } }
内部拦截法不太好理解,比较饶。
外部拦截法与事件分发机制流程一致,简单好理解,所以一般情况下都会优先选择外部拦截法。