任何程序都是静态代码,我们把这些静态代码打包好,然后放到运行环境当中,通过事件流的驱动使这些代码运行起来。Android的环境也不例外。
静态的代码,在动态事件的驱动下,才会有效的运转起来。
驱动Android程序运行起来的事件大致可以分为以下几种:
用户事件:如点击屏幕,滑动等各种手势;
系统事件:如屏幕方向的转变;
线程通讯事件:线程之间互发消息,程序根据消息内容进行相应的响应;
进程通讯事件:这里的进程包括本程序开启的进程,也包括其他应用程序的进程。
而android程序探测事件是否发生的途径,主要有两类:监听和回调。
监听:
如设置一个监听器,用来监听某个控件是否被点击了。监听器可以是内部类的一个对象,外部类的一个对象(不常用,因为不能很好的获得界面上对象的引用),也可以是activity本身。
回调:
写一个回调方法即可,当某个事件发生时,系统就会调用这个方法。
对于基于监听的处理模型来说,事件源与事件监听是分离的,当事件源上发生特定事件时,该事件交给事件监听器负责处理;对于基于回调的事件处理模型来说,事件源和事件监听器是统一的,当事件源发生特定事件时,该事件还是由事件源本身负责处理。
下面从四种事件分类的角度具体探讨:
用户事件:
以button的点击事件为例。从事件监听器角度,可以设置内部类、外部类、匿名内部类作为事件监听器,或者直接绑定标签(在xml文件中button标签下增加一行:android:onClick=“clickHandler”,然后在其对应的activity中实现clickHandler方法即可);从回调的角度,基于回调的事件处理机制可通过自定义view来实现,在自定义View中重写该view的事件处理方法即可。
那么问题来了,如果一个自定义button既自己重写了事件处理方法,又设置了事件监听器,那会怎么执行呢?
答案是先执行事件监听器的方法,再执行重写的方法。为什么会是这样呢?这要从android的用户事件的分发机制上来说了。
这边博文讲的不错,可以参考下:http://blog.csdn.net/sinyu890807/article/details/9097463#comments
用户事件的分发,从严格意义上来说,分为事件的分发和事件的处理。
事件的分发用于找到处理事件的对象,事件的处理用于找到事件的处理方式。
具体来说,用户在界面的上触发了某个操作,而界面上是有很多元素的,并且各个元素之间还是有层级关系的那么最终这个操作事件交给哪个控件来处理呢?
我们先抛开用于监听的事件,比如按钮的点击的监听等,先把讨论的范围限定在可以回调的事件onTouchEvent,上边已经把回调和监听的区别说的很详细了,我们不需要设定任何监听对象就可以实现onTouchEvent的调用,只要在类中重写onTouchEvent即可,当用户发生touch操作的时候,就会自动调用这个方法。
好,我们先来看下下边这张图:
图1
先来说说 onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent 这三个函数。activity中只有:dispatchTouchEvent、onTouchEvent。viewgroup中有:onInterceptTouchEvent、dispatchTouchEvent,因为viewgroup继承于view,所以其实也有OnTouchEvent方法。
onInterceptTouchEvent 用来设定消息是否被阻断,如果是,返回true,不是,返回false。
dispatchEvent用来向下层view或者viewgroup分发消息,如果下层 view或者viewgroup中的dispatchEvent方法都返回false,则调用自身的onTouchEvent方法(其实是调用的super.dispatchEvent方法,因为viewGroup的super是view,view的dispatchEvent方法实际上是调用的ontouch方法,所以最终调用的还是onTouchEvent方法)。
OnTouchEvent方法用于消费掉事件,及不让事件继续向后传递。(其实dispatchEvent也能消费事件,再在后边的设置监听的时候会讲到)
view中的dispatchTouchEvent方法其实最终还是调用的onTouchEvent方法。如下图源码:
好了,干货开始了。我刚才说了事件的处理实际上分为两个阶段,第一个阶段是寻找可以处理事件的对象,第二个阶段是处理事件的对象对事件进行处理。
现在说的是第一个阶段。寻找处理事件的对象是通过获取第一个响应down事件的控件进行的。如果某个控件响应的down事件,那么之后的move和up事件都会由这个控件处理。那么怎么获取第一个响应down事件的控件呢?
借助图1来说,首先dalvik虚拟机讲事件包装成MotionEvent类,传给当前activity的dispatchTouchEvent方法,然后activity分发给它的viewgroup对象,之后逐层调用dispatchtouchEvent方法向下分发,只要有一个onTouchEvent方法返回true,就停止分发操作(实际上是dispatchTouchEvent方法调用onTouchEvent方法,返回的true)。之后的所有操作(up 、move)就交由onTouchEvent返回true的那个控件处理。如果某个groupview调用所有子view的dispatchTouchEvent方法都返回false,他就调用自身的onTouchEvent方法。(实际上及调用了父类-view的dispatchTouchEvent方法,view的dispatchTouchEvent的方法)。如果onTouchEvent方法没有重写,因为onTouchEvent方法在view默认返回false,不会对事件流产生影响。重写了OnTouchEvent方法但是返回flase,也是会捕获下down事件,不会拦截后续的move和up事件。
总结下,抛开设置监听事件不说,单从onTouchEvent的响应来看。如果想要在一个控件上拦截一个用户事件,只需要重写它的onTouchEvent方法,并且返回true即可。如果只是想获取down的这个事件,并不拦截,只需要重写onTouchEvent方法,但是返回false即可。如果一个viewGroup对事件进行了拦截(onInterceptTouchEvent返回了true,因为onInterceptTouchEvent在dispatchtouchEvent内部,所以dispatchTouchEvent就会终止向下分发这个事件,交由viewGroup的onTouchEvent处理down事件,和之后产生的move和up事件,如下图源码解析)
好,上边是抛开了监听来说的,下面来进一步说说有监听的情况。
如果我给一个Button设置了click监听,touch监听,又重写了onTouchEvent方法,会先执行哪个呢?
我们先从原理上来简单解释下,前边讲回调和监听的原理的时候已经说过,监听的优先级大于回调的优先级(这些是在代码中控制的,待会儿会来分析源码)。所以touch监听肯定是比onTouchEvent要优先执行。通过下图的源码进行分析:
如果设置了touch监听,该控件是enable的,touch对应的函数返回true,这个事件就会被这个监听丢向拦截掉,不会再向后传播了。touch对应的监听函数返回了false,就还是会向后传播的,touch只会捕获到down的事件。
而onclick事件对应的监听的执行,其实是在ontouch中的
performClick中是这样的
所以说如果设置了onclick事件的捕获,必须保证button控件成功拦截了该用户事件,并且之后的捕获了up事件。
如果重写了onTouchEvent方法,这里面对onclick处理的逻辑就会被覆盖掉,所以设置了Onclick监听也会失效的。
在ontouchEvent不重写的前提下,无论设不设置onclick监听,只要这个控件是可以响应onclick的,(如果setOnclickEnable(false)就不行了)onTouchEven都t会返回true,消费掉这个事件的。具体源码实现,可以参考如下:
上如标出的部分是检查是否是可以响应click事件(仅仅 是可不可以响应,至于有没有设置click监听,以及如何调用监听对象的方法,就在switch中进行处理了)。如果为真,则最后OnTouchEvent都返回true如下图:
总结一下,如果我们对一个控件同时设定了OnTouchListener、重写OnTouchEvent、OnclickListener,执行顺序是OnTouchListener、重写的OnTouchEvent。或者OnTouchListener、OnclickListener.具体对源码内容的解析,可以参看下图:
关于用户事件流,这几篇文章写的也很好,可以借鉴一下:http://www.cnblogs.com/linjzong/p/4191891.html
http://www.jianshu.com/p/34cb396104a7
关于系统事件、进程间通讯事件、线程间通讯事件将在后边的文章中介绍。