Android事件拦截机制分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq347198688/article/details/52680091

转载请注明出处:
http://blog.csdn.net/qq347198688/article/details/52680091
本文出自【何嘉龙的博客

1.事件拦截机制产生原因

当Android系统捕捉到用户的各种输入事件后,如何准确地传递给真正需要这个事件的控件呢?Android给我们提供了一整套完整的事件传递、处理机制,来帮助开发者完成准确的事件分配与处理。

那么问题来了,android事件拦截机制中的事件指的是什么事件?并不是所有事件都会有拦截机制的。这个事件指的是触摸事件,顾名思义,触摸事件就是捕获触摸屏幕后产生的事件。当手点击按钮时,通常会产生两个或三个事件——按钮被按下,这是事件一;如果手滑动一下,就会产生滑动事件,这是事件二;当你手抬起来的时候,就会发生事件三。可能我这样跟你们解释你们还不太清楚,但是接下来我跟你讲到一个跟触摸事件相关的类,你们就应该清楚了。MotionEvent,如果大家重写过onTouchEvent()方法,就应该很熟悉吧,因为里面的参数正是MotionEvent。

我们都知道,Android的View结构是树形结构,不清楚的童鞋可以看下这篇博客Android控件架构以及setContentView()方法剖析,也就是说,View可以放在ViewGroup里面,通过不同的组合来实现不同的样式。那么问题又来了,View放在一个ViewGroup里面,这个ViewGroup又放在另一个ViewGroup里面,甚至还有可能继续嵌套,一层层叠起来。可我们的触摸事件就一个,也就是只能被一个View消费,不能多个View一起消费,那到底该分给谁呢?对于同一个事件,儿子View跟爸爸ViewGroup都想进行处理,但是决定权又在妈妈(开发者)手里。因此,这就产生了事件拦截机制。

2.事件拦截机制详细分析

当然,事件拦截机制可以很复杂,也可以很简单。但是初学者总是会卡在这里,面对这类问题总是很畏惧,因此我们不涉及源码,通过最直观的Log信息,让大家有一个大致的了解,知道事件拦截的本质,然后自己在结合源码分析的时候会更加清晰。

事件拦截机制其实设计的非常人性化,我们可以从身边的例子看出实际的本质:假设你妈妈今天很累,不想做家务,然后叫你爸爸做;你爸爸他也不想做,他就要你做,并给了一个很好的理由,要替父母承担点责任,从家务事开始吧。就算你再不情愿,也只能做了吧。当你做完的时候,你得告诉你爸爸,你才能玩一会电脑,然后你爸爸再告诉你妈妈,他才能看球赛。在这个生活的例子中,家务事就是我们的触摸事件,然后爸爸、妈妈还有你三个作为View需要对这件事件进行处理。其实,这就是我们整个事件拦截机制的本质。事件传递由父控件向子控件传递,事件处理由子控件向父控件传达。

这里写图片描述

妈妈——相当于最外层的ViewGroup
爸爸——相当于中间的ViewGroup
你——相当于最里面的View

代码非常简单,只是重写了事件拦截和处理的几个方法,并给他们加上了一些Log而已。

对于ViewGroup来说,重写如下所示的三个方法。


@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.e(TAG, "--dispatchTouchEvent--mother");
    return false;
}

@Override  
public boolean onInterceptTouchEvent(MotionEvent ev) {  
    Log.e(TAG,"--onInterceptTouchEvent--mother");  
    return false;  
}  

@Override  
public boolean onTouchEvent(MotionEvent event) {  
    Log.e(tag,"--onTouchEvent--mother" );  
    return false;  
}  

而对于View,重写如下的两个方法就行了。

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.e(TAG, "--dispatchTouchEvent--you");
    return false;
} 

@Override  
public boolean onTouchEvent(MotionEvent event) {  
    Log.e(tag,"--onTouchEvent--you" );  
    return false;  
}  

从上面的代码可以看出来,ViewGroup级别比较高,比View多了一个方法——onInterceptTouchEvent()。这个方法看名字就知道是拦截事件的方法。

Log结果如下所示:

--dispatchTouchEvent--mother
--onInterceptTouchEvent--mother
--dispatchTouchEvent--father
--onInterceptTouchEvent--father
--dispatchTouchEvent--you
--onTouchEvent--you
--onTouchEvent--father
--onTouchEvent--mother

可以看见,正常情况下,事件的传递顺序是:

妈妈 → 爸爸 → 你。事件传递的时候,先执行dispatchTouchEvent()方法,再执行OnInterceptTouchEvent()方法。

然后再执行事件的处理,顺序是:

你 → 爸爸 → 妈妈。事件都是在处理onTouchEvent()方法。

可能大家都注意到了这些方法的返回值都是false,其实这是我为了方便演示才这样做的。onOnInterceptTouchEvent()中的true,代表拦截,也就是不往下传递了,false,表示不拦截。而onTouchEvent()中的true,表示消费此事件,也就是全权处理此事件,不往上面传达了。当然,系统默认为false。可以看到,当全部为false的时候,事件传递顺序跟事件执行的顺序刚好是相反的。


嘿嘿,这个时候心里是不是有了疑问,假如我们改变下返回值,这个时候Log的结果会是怎么样的。我们接着来试验,我把妈妈的OnInterceptTouchEvent()的返回值,改为true,其他的都不变,还是false。也就是妈妈那天突然改变主意了,为了让你们爷俩好好休息,她决定自己做家务事,所以,事件传递顺序就会是这样:

妈妈

事件处理机制:

妈妈

所以Log日志我们可想而知,就会是这样的:

--dispatchTouchEvent--mother
--onInterceptTouchEvent--mother
--onTouchEvent--mother

嘻嘻,是不是都想到了,妈妈一个人把事情都干完了,当然不会轮到我们了。但是如果有一天,爸爸突然善心大发,看妈妈太辛苦了,想帮妈妈做家务,把家务揽下来要自己做。也就是把爸爸的onInterceptTouchEvent()的返回值,改为true,其他的都为false。那么事件传递顺序就会是这样:

妈妈 → 爸爸

事件处理顺序:

爸爸 → 妈妈

Log会是这样:

--dispatchTouchEvent--mother
--onInterceptTouchEvent--mother
--dispatchTouchEvent--father
--onInterceptTouchEvent--father
--onTouchEvent--father
--onTouchEvent--mother

嘻嘻,相信大家看到这里都懂了吧。

那还没完呢,onTouchEvent()里面的返回值不也都是false,如果把它里面的返回值改为true,会发生什么呢?

好的,那我们还是通过Log来探讨。

因为onTouchEvent事件,也就是事件处理顺序,是由子控件传给父控件的,也就是由你传给爸爸,然后爸爸又传给妈妈。那我们把你的onTouchEvent()方法的返回值改为True。来让我们看下Log:

--dispatchTouchEvent--mother
--onInterceptTouchEvent--mother
--dispatchTouchEvent--father
--onInterceptTouchEvent--father
--dispatchTouchEvent--you
--onTouchEvent--you

我们通过Log可以看到,事件传递顺序还是不变的,因为我们的onInterceptTouchEvent()的返回值都为false,没改变,但是事件处理机制好像变化了,你并没有传给你爸爸,故也没有你爸爸传给你妈妈。也就是当返回为True的时候,这件事就相当于你全权处理,不用再跟你爸爸或者妈妈说了。

如果把你爸爸的onTouchEvent()方法的返回值改为True,其他的不变,Log的结果就会是这样的:

--dispatchTouchEvent--mother
--onInterceptTouchEvent--mother
--dispatchTouchEvent--father
--onInterceptTouchEvent--father
--dispatchTouchEvent--you
--onTouchEvent--you
--onTouchEvent--father

相信大家看到这里,应该对android事件拦截机制有了很深的了解吧。希望本篇博客能对大家有所帮助。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页