View中的滑动冲突
1.滑动冲突的种类
滑动冲突一般有3种,
第一种是ViewGroup和子View的滑动方向不一致
比如:
父布局是可以左右滑动,子view可以上下滑动
第二种
ViewGroup和子View的滑动方向一致
第三种
第三种类似于如下图
2.滑动冲突的解决方式
滑动冲突一般情况下有2种解决方法
1.外部拦截法
2.内部拦截法
这两个的区别是:外部拦截法是先经过父容器的拦截处理,如果父容器拦截的话,则拦截。不拦截则交给view。
而内部拦截法一般情况下是父容器不进行拦截,直接由子容器进行判断拦不拦截。
3.分析
这里分析就只分析第一种滑动冲突和第二种滑动冲突,第三种就是把1,2结合起来
3.1第一种滑动冲突
先讲外部拦截法
3.1.1外部拦截法
在ViewGroup里面重写onInterceptTouchEvent()
在ACTION_DOWN中先判断ViewGroup是否滑动,如果滑动的话,则直接拦截
intercepted = true;
没有滑动的话,不进行拦截
intercepted = false;
这块的完整代码
case MotionEvent.ACTION_DOWN:{
intercepted = false;
if(!mScroller.isFinished()){
mScroller.abortAnimation();
intercepted = true;}
}
**mScroller.abortAnimation();**中止动画
因为如果用户现在在水平滑动,在动画结束前就竖直滑动。不加上面那句代码会导致界面停在中间状态
然后在ACTION_MOVE中进行判断
判断竖着滑动的长度长还是横着滑动的滑动长
图中是ViewGroup是左右滑动,view是上下滑动
如果左右滑动的长度>上下滑动的长度,则直接拦截;否则ViewGroup不进行拦截
int deltax = x - lastX;
int deltay = y - lastY;
if(Math.abs(deltax)>Math.abs(deltay)){
intercepted = true;
}
else{
intercepted = false;
}
然后ACTION_UP中
case MotionEvent.ACTION_UP:{
intercepted = false;
break;
}
这里必须为false,因为ACTION_UP本身没多大意义
3.2.1内部拦截
内部拦截是子view重写dispatchTouchEvent()
并配合**requestDisallowInterceptedTouchEvent()**方法
在ACTION_DOWN中
mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);
这个mHorizontalScrollViewEx2是那个自定义ViewGroup
这里就是ViewGroup不进行拦截
在ACTION_MOVE中
int deltax = x - lastX;
int deltay = y - lastY;
if(Math.abs(deltax)>Math.abs(deltay)){
mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);
}
如果左右滑动的长度大于上下滑动的长度,则ViewGroup进行拦截
在ACTION_UP
直接
break;
最后在
return super.dispatchTouchEvent(event);
其实已经结束了,但是我们为了优化性能
在自定义的ViewGroup中进行如下操作
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if(action == MotionEvent.ACTION_DOWN){
if(!mScroller.isFinished()){
mScroller.abortAnimation();
intercepted = true;
}
return false;
}else{
return true;
}
}
我对else那块的理解是
如果ViewGroup开始判断action == MotionEvent.ACTION_MOVE或者ACTION_UP
那就说明action肯定拦截了MotionEvent.ACTION_DOWN,这样的话。那么其他的行为它肯定也会拦截
在不重写其他方法的情况下:
不存在一个ViewGroup拦截同一个事件的ACTION_DOWN,而它的子View拦截同一个事件的ACTION_MOVE/ACTION_UP
也不存在一个子View拦截同一个事件的ACTION_DOWN,而它的ViewGroup拦截同一个事件的ACTION_MOVE/ACTION_UP
3.2第二种滑动冲突
这个得你自己定义
假设我的ViewGroup和子View都是上下滑动
我的ViewGroup和子View定的规矩是子View滑到底才由ViewGroup进行拦截,其他都是子View拦截
先看看外部拦截法:
3.2.1外部拦截法
大致思路就是在重写ViewGroup的onInterceptTouchEvent是进行判断
如果上下滑动的距离超过了子View的长度,则onIntercept = true,其他情况下都为false
private float startY;
private View innerView;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 获取内部View
innerView = getChildAt(0);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getY();
// 不拦截,由内部View处理
return false;
case MotionEvent.ACTION_MOVE:
float currentY = ev.getY();
float deltaY = currentY - startY;
startY = currentY;
// 内部View滑动到顶部且向上滑动,或内部View滑动到底部且向下滑动时拦截事件
if ((innerView.canScrollVertically(-1) && deltaY > 0) ||
(innerView.canScrollVertically(1) && deltaY < 0)) {
// 拦截事件,由外部ViewGroup处理
return true;
}
break;
}
// 不拦截,由内部View处理
return false;
}
3.2.2内部拦截法
在重写子View的**dispatchTouchEvent()**的时候,和上面差不多,也是进行判断
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (滑动到底部的条件) {
return false; // 不拦截事件,交给父容器处理
} else {
return super.dispatchTouchEvent(event); // 继续传递给子View处理
}
}
3.3第三种滑动冲突
第三种滑动冲突的解决方法就是结合第一和第二两个就行了