转载请注明出处:
http://blog.csdn.net/qq347198688/article/details/52680091
本文出自【何嘉龙的博客】
当Android系统捕捉到用户的各种输入事件后,如何准确地传递给真正需要这个事件的控件呢?Android给我们提供了一整套完整的事件传递、处理机制,来帮助开发者完成准确的事件分配与处理。
那么问题来了,android事件拦截机制中的事件指的是什么事件?并不是所有事件都会有拦截机制的。这个事件指的是触摸事件,顾名思义,触摸事件就是捕获触摸屏幕后产生的事件。当手点击按钮时,通常会产生两个或三个事件——按钮被按下,这是事件一;如果手滑动一下,就会产生滑动事件,这是事件二;当你手抬起来的时候,就会发生事件三。可能我这样跟你们解释你们还不太清楚,但是接下来我跟你讲到一个跟触摸事件相关的类,你们就应该清楚了。MotionEvent,如果大家重写过onTouchEvent()方法,就应该很熟悉吧,因为里面的参数正是MotionEvent。
我们都知道,Android的View结构是树形结构,不清楚的童鞋可以看下这篇博客Android控件架构以及setContentView()方法剖析,也就是说,View可以放在ViewGroup里面,通过不同的组合来实现不同的样式。那么问题又来了,View放在一个ViewGroup里面,这个ViewGroup又放在另一个ViewGroup里面,甚至还有可能继续嵌套,一层层叠起来。可我们的触摸事件就一个,也就是只能被一个View消费,不能多个View一起消费,那到底该分给谁呢?对于同一个事件,儿子View跟爸爸ViewGroup都想进行处理,但是决定权又在妈妈(开发者)手里。因此,这就产生了事件拦截机制。
当然,事件拦截机制可以很复杂,也可以很简单。但是初学者总是会卡在这里,面对这类问题总是很畏惧,因此我们不涉及源码,通过最直观的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事件拦截机制有了很深的了解吧。希望本篇博客能对大家有所帮助。