场景
外部滑动方向和内部不同:
左右滑动让父容器的View拦截点击事件来处理,上下滑动让内部的View拦截点击事件来处理
可以根据滑动的距离差,滑动速度或者角度来进行判断
解决套路
外部拦截法
重写父容器的事件拦截方法
如果此时想要横向滑动,父容器拦截该事件,在onTouchEvent方法处理;想要竖向滑动,父容器就不拦截,交给子元素处理
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();
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;
}
default:
break;
}
mLastXIntercept = x;
mLastYIntercept = y;
return intercepted;
}
注意:
- down不要拦截,一旦拦截后续都会交给父容器处理,那样如果不符合就无法交给子元素处理了
内部拦截法
重写子容器的事件分发方法和父容器的事件拦截方法
父容器不再拦截任何事件,都交给子元素处理。那么在子元素的dispatchTouchEvent
方法中,如何当前想要竖向滑动就直接讲此事件消耗掉。如果是横向滑动,就调用requestDisallowInterceptTouchEvent
方法,
下面是子元素的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;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
同时父View需要重写onInterceptTouchEvent方法:
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
注意:为何ACTION_DOWN要返回false?
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
return;
}
// 传入true,设置该标志,否则清楚该标志
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// 然后调用父视图的该方法
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
在ViewGroup的dispatchTouchEvent方法中
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// requestDisallowInterceptTouchEvent设置的true,导致这里的disallowIntercept为false
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 本来设置的true,不让父容器拦截,但这里却进入了
intercepted = onInterceptTouchEvent(ev);
// 所以我们重写onInterceptTouchEvent方法让他返回false,其他为false
ev.setAction