上一篇博客讨论了一下view中的事件分发,既然存在事件分发的过程,那么也就可能存在着冲突。常见的由以下三种形式的冲突。(外面叫做OuterViewGroup
,包裹在里面的叫做InnerViewGroup
)
OuterViewGroup
和InnerViewGroup
滑动方向不同。(比如OuterViewGroup
左右滑动,InnerViewGroup
上下滑动)。OuterViewGroup
和InnerViewGroup
滑动方向相同。(比如两者都是上下滑动)。- 以上两种情况的混合。
当然,实际上我们需要解决的只是前两种的滑动冲突。(因为第三种只是前两种的混合)。
第一种OuterViewGroup
和 InnerViewGroup
滑动方向不同,这种场景还是比较常见的。比如ViewPager
嵌套Fragment
,Fragment
里面再嵌套一个需要滑动的东西比如ListView
。但是我们在实际项目当中如果有这种组合场景,为什么没有冲突呢,这是因为ViewPager
的代码中已经帮我们处理好这种冲突了。
那么如果我们当中有这种情况,又应该如何分析处理呢?假设OuterViewGroup
需要左右滑动,InnerViewGroup
需要上下滑动。当我们手指触摸(或点击)屏幕时,我们自己心里肯定知道我们的目的,是想让它左右滑动还是上下滑动,那么问题是程序怎么判断我们是左右滑动还是上下滑动呢。(首先要明白我们是不可能在屏幕上画出一条完全垂直或者完全平行的轨迹的)。程序常见的判断方式有几种。比较X轴或Y轴上滑动距离,比较X轴或Y轴上滑动速度。比较滑动的轨迹与坐标轴的夹角。而第一种判断两个方向的距离是比较简单常用的方式。
第二种OuterViewGroup
和 InnerViewGroup
滑动方向相同。那么此时就没有办法根据事件或轨迹在X轴Y轴的速度距离等指标进行判断了。但是此时我们肯定能根据具体的业务场景来做判断(否则这业务或产品设计的不是扯淡嘛)。什么时候该OuterViewGroup
滑动,什么时候该InnerViewGroup
滑动,这些都要视具体业务场景判断。
针对第一种情况,OuterViewGroup
左右滑动,InnerViewGroup
上下滑动这种滑动冲突,我们常见的一种解决方案就是在 OuterViewGroup
中(也就是父元素中),通过其onInterceptTouchEvent
来控制事件应该是自己来处理呢,还是将事件分发给子view(也就是InnerViewGroup
来处理)。
这种处理逻辑用伪代码来表示如下:
//记录我们上一个事件中滑动的距离,这是为了进行进一步的判断
int mLastInterceptX = 0;
int mLastInterceptY = 0;
public boolean onInterceptTouchEvent(MotionEvent event){
boolean intercept = false;
int x = event.getX();
int y = event.getY();
switch(event.getAction){
case MotionEvent.ACTION_DOWN;
/**
* 要搞清楚,我们是不能拦截ACTION_DOWN事件的。
* 如果在一个事件序列当中,我们拦截了ACTION_DOWN,那么该序列中剩下的事件也会交给该View进行处理,
* 不会再调用 onInterceptTouchEvent()方法来询问是否拦截事件了。
*/
intercept = false;
break;
case MotionEvent.ACTION_MOVE;
//判断是否需要拦截处理事件
if (interceptCondition()){
intercept = true;
} else {
intercept = false;
}
break;
case MotionEvent.ACTION_UP;
//我们常用的onClickListener就是判断的ACTION_UP,所以这里也返回false。
//这样就不会影响 点击(包含长按)事件的处理。
intercept = false;
break;
default:
}
mLastInterceptX = x;
mLastInterceptY = y;
return intercept;
}
//
/**
* 判断父元素即OuterViewGroup是否需要拦截处理事件。
* 常用的判断方式由几种,比如根据X轴Y轴的距离, 速度,夹角等
*/
public boolean interceptCondition(MotionEvent event){
//我们假设OuterViewGroup需要左右滑动,我们根据 X轴和Y轴的距离来判断。
int distanceX = Math.abs(mLastInterceptX - event.getX());
int distanceY = Math.abs(mLastInterceptY - event.getY());
if ( distanceX > distanceY){
return true;
} else {
return false;
}
}
以上就是关于解决滑动冲突的一种常见的方法,除此之外,还有一种方法,就是在InnerViewGroup
当中使用requestDisallowTouchEvent
来干涉父元素中的事件分发过程,不过这种方法我没有理解透彻,所以就不做阐述了。另外,其实相比之下,第一种方法也更加的简单易懂。
至于第一种方法的demo,参见:https://github.com/yaowen369/BlogDemo/tree/master/Android/AndroidBlogDemo/app/src/main/java/com/yaoxiaowen/android_blog_demo/dispatch_event/slide_conflict
处的代码
备注:本篇博客的内容,均是参考了 任玉刚 的《Android开发艺术探索》的相关章节。
github:
https://github.com/yaowen369
欢迎对于本人的博客内容批评指点,如果问题,可评论或邮件(yaowen369@gmail.com)联系
<p >
欢迎转载,转载请注明出处.谢谢
</p>
<script type="text/javascript">
function Curgo()
{
window.open(window.location.href);
}
</script>