对于滑动冲突,相信安卓开发的人都会有这种体会:本来从网上下载的demo运行得好好的,但是只要出现滑动冲突,demo就无法正常工作了。许多开发者面对滑动冲突都会显得束手无策,(包括小编)于是小编就赶紧到处学习方法,并整理了下面的滑动冲突类型及解决方法,希望对和小编一样的初学者有帮助。
常见的冲突场景
- 场景一:外部滑动方向和内部滑动方向不一致
在主流应用中,几乎都会出现这种效果:首页可以通过左右滑动来切换页面,而每个页面内部往往又是一个ListView,这种情形下是有滑动冲突的(但是ViewPager内部处理了这种滑动冲突)
处理规则
—— 根据用户滑动的方向决定让内部View还是外部View拦截点击事件
- 场景二:外部滑动方向和内部滑动方向一致
在这类场景里,可以想象父View可以通过上下滑动切换页面,然而内部还是一个ListView,当用户滑动的时候系统不知道该将滑动事件分发给谁
处理规则
—— 根据业务的规定决定让内部View还是外部View拦截点击事件
比如说上述例子,可以采用内部拦截法,优先将滑动事件交给内部ListView处理,当ListView已经到达顶部或者底部已经无法再进行同方向的滑动时,就将滑动事件交回给外部View处理,实现换页功能。
- 场景三:上面两种情况的嵌套
这类场景也很常见,假如你的首页是一个ViewPager,可以来回切换不同页面,每个页面中又有各自的ListView,然后其实还隐藏着一个SlidingMenu在左侧。
处理规则
—— 根据业务的规定决定让内部View还是外部View拦截点击事件
主要是最SlidingMenu的处理,业务上可以要求当用户的向右滑动是从app最左侧边缘开始时才将事件交给SlidingMenu处理。其他部分可以跟第一个场景一样。
常用解决方式
- 外部拦截法 —— 点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要此事件就不拦截。
外部拦截需要重写父容器的onInterceptTouchEvent方法,伪代码如下:
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:{
// 对于ACTION_DOWN这个事件,父容器必须返回false,因为父容 器一旦拦截了
// ACTION_DOWN,后续的ACTION_MOVE和ACTION_UP也都会直接交由父容器处理
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;
}
- 内部拦截法 —— 指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理。
内部拦截需要重写子元素的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 deltxY = y - mLastY;
if (父容器需要当前点击事件) {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP:{
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
另外,父容器也要做如下修改:
public boolean onInterceptTouchEvent(MotionEvent event){
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}