滑动冲突一般是在界面中,内外两层同时可以滑动,这个时候就会产生滑动冲突。
1、常见的滑动冲突场景
场景1:外部滑动方向和内部滑动方向不一致
场景2:外部滑动方向和内部滑动方向一致
场景3:场景1和2嵌套
分别如下:
场景1主要是将ViewPager和Fragment配合使用,达到页面滑动的效果,可以通过左右滑动,切换页面,而在每个页面又会又ListView。如果使用的是ViewPager,就不需要注意这个问题,因为ViewPager内部已经处理了这个冲突。如果是Scroll View等,就需要我们手动处理这个滑动冲突。
场景2,出现在页面中,两层滑动在同一个方向,因为在滑动化成中,系统无法判断是滑动那一层,要么就是两层同时滑动,显得很卡顿,要么只有一层滑动。一般就上下同向,或者左右同向。
场景3,是场景1和场景2的嵌套,相当于一个LlideMenu效果,内部有一个ViewPager,ViewPager的内部又有一个ListView。这种其实是单个简单的冲突叠加,只需要处理好内部和中间层的冲突,中间层和外层的冲突即可。
2、滑动冲突的处理规则
场景1:当用户左右滑动时,需要让外部的View拦截,当上下滑动时,让内部的View拦截。这样根据特征,即根据水平和竖直方向滑动的距离差、两个方向的夹角、两个方向的速度差,来判断是由谁来拦截事件。
场景2:场景2比较特殊,因为它无法根据滑动的角度、距离差以及速度差判断。但是这种情况一般是业务功能上有特殊需求,比如达到什么状态,让外部View响应,另一个状态,让内部View响应等。这样我们就可以参照场景一做相应的拦截。
场景3:处理方式和场景1、2类似,也需要参照具体的业务需求。
3.滑动冲突的拦截方式
3.1外部拦截
外部拦截,其实就是所有点击事件都先经过父容器拦截处理,如果父容器需要此事就拦截,如果不用就不拦截。这样符合滑动冲突的问题,也比较适合事件的分发机制。外部拦截的方法是需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可:
public boolean onInterceptTouchEvent(MotonEvent 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;
}
default:
break;
}
mLastXInrercept = x;
mLastYIntercepr = y;
return intercepted;
}
上述代码是外部拦截得典型逻辑,针对不同得滑动冲突,只需要修改父容器需要当前点击事件得条件即可。注意得一点,当ACTION_DOWN这个事件发生,父容器必须返回false,即不拦截,原因是因为如果父容器拦截,那么后续得事件都会交由父容器处理。ACTION_UP,同理,也需返回false,因为,如果父容器得UP事件返回true,而事件又需要交给子View去响应,那么子元素得onClick事件就无法触发。
3.2、内部拦截
内部拦截是指父容器不做任何拦截,所有事件都交由子元素处理,如果子元素需要处理,直接消耗,如果不处理,再交由父容器,这种方式和Android中的事件分发机制不一致,需要配合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;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
上面代码是典型的内部拦截方法,除了子元素要处理以外,父元素也要默认拦截除了ACTION_DOWN以外的其它事件,这样当子元素调用parent.requestDisal-lowIntercepteTouchEvent(false)方法时,父元素才能继续拦截所需要的事件。
特别声明:内容总结来源《Android开发艺术探索》,仅记录学习,如有侵权或不对之处,还请告知,定当删除或改正