Learn && Live
虚度年华浮萍于世,勤学善思至死不渝
前言
Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/c1S1X,话不多说我们马上开始!
1.滑动冲突场景
(1)外部与内部滑动方向不一致
(2)外部与内部滑动方向一致,常见,如ScrollView嵌套RecyclerView
(3)上面两种情况的嵌套
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5nMNbGBc-1657196736636)(F:\yhy925\dailylife\Work\2.Android\《Android开发艺术探索》笔记\4.View\5.View冲突场景.png)]
2.滑动冲突的处理规则
(1)针对场景1,左右滑动时让外部的View拦截点击事件,上下滑动时让内部的View拦截点击事件
(2)针对场景2,可以根据业务的不同需求进行处理,如当处于某种状态时需要外部View响应用户的滑动,而处于另一状态时则需要内部View来响应滑动
(3)针对场景3,与场景2类似,无法直接根据滑动的角度、速度差、距离差来判断,只能从业务的需求上得出相应的处理规则
3.滑动冲突的解决方式
外部拦截法
指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要此事件就不拦截。此方法需要重写父容器的onInterceptTouchEvnet方法,在内部做相应的拦截
//此方法一般用于事件拦截
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean interceoted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
interceoted = false;
break;
case MotionEvent.ACTION_MOVE:
// 判断是否拦截
if (满足父容器拦截要求){
interceoted=true;
}else{
interceoted=false;
}
break;
case MotionEvent.ACTION_UP:
interceoted=false;
break;
}
mLastXIntercept = x;
mLastYIntercept = y;
return interceoted;
}
(1)不可以拦截ACTION_DOWN事件,因为一旦拦截则后续的MOVE、UP事件都会直接交由父容器处理,无法传递给子元素
(2)ACTION_MOVE事件根据需要决定是否拦截
(3)ACTION_UP必须返回false,如果返回true,并且滑动事件交给子View处理,那么子View将收不到 ACTION_UP事件,子View的 onClick事件也将无法触发。而父View不一样,如果父View在 ACTION_MOVE中开始拦截,除了一个ACTION_CANCEL传递给子View,那么后续的所有都将默认交给父View处理,所以ACTION_UP父View还是可以收到。
内部拦截法
指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就拦截,否则交给父容器处理。由于涉及子元素控制父容器是否拦截,需要调用requestDisallowInterceptTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 禁用父布局拦截事件,从而失去后续Action(即失去)
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int deltaX=x-mLastX;
int deltaY=y-mLastY;
if (父容器需要此类点击事件){
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
}
mLastX=x;
mLastY=y;
return super.dispatchTouchEvent(ev);
}
父容器也需要重写onInterceptTouchEvnet()方法,拦截除ACTION_DOWN以外的其他事件
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
(1)父容器不能拦截DOWN事件,因为ACTION_DOWN事件不受标记位FLAG_DISALLOW_INTERCEPT的限制,所以一旦父容器拦截了DOWN事件,后续事件都无法传递到子元素中去
(2)内部拦截法的操作要稍微复杂一些,因此更推荐使用外部拦截法来解决常用的滑动冲突