记得以前教我code的启蒙老师对我说过,如果你想学习一个新事物只需要三个W:Why、What、How,也就是:为什么?是什么?怎么做?当你搞懂这三个W之后,你对新事物必定会有一个透彻的了解。然而对大多数Student来说他们往往最有兴趣的是“How”也是他们最先接触的一个W,其次则是“What”,而“Why”往往会被选择性地忽略,这也是很多时候我们为什么无法将现有知识水平提升一个层次的根本原因。
同样地,对于事件分发,我先不打算直接切入主题教大家如何如何看源码如何如何分析Android工程师们复杂的思维逻辑,如何如何去理清错综交杂的抽象层次。按照我们上面所说,我们先来试着追寻Android设计师们的思考方式尝试去理解为什么要有事件分发,大家都知道事件分发是Android事件处理的一个Breaking Point,难以理解,既然如此为什么要把这么复杂的东西作为Android的一部分呢?既然需要,那么其必定是必不可缺无法撼动的,but why?淡定,跟随大侦探爱尔摩斯来追寻这不为人知的丑陋秘密。
我们都知道,我们的手机或其他的手持设备甚至显示设备等等其显示区域都是有限的,即便是股票的大盘显示屏也是有限的,没有无限的视屏,更何况是巴掌大的一块显示区域。假设你是Android的一名设计师,要你来确定一种界面元素的显示方式,那么按照人传统的操作习惯和思维方式,你有且仅有两种选择:
Chioce A:
将界面元素以逐个排列的方式显示在屏幕上,如下图:
该方式可以让所有的界面元素都有属于自己独自的空间,对系统来说管理这些空间要简便得多,因为它们占有确定的屏幕区域,只需在显示时记录这些Area则可,各个元素互不干预。但是,瞎子也能看出来这样的显示方式有个明眼的弊端,随着界面元素的增加,一旦撑满屏幕,所有的元素大小都将被不断调整以便能放入更多的界面元素,而且,如果我们想将元素以叠加的方式排列,这样的显示方式是无法做到的,So,pass it。
Chioce B:
将界面元素层叠显示,每个元素理论上都拥有与显示设备尺寸一致的区域,如下图:
这么一来,我们可以在有限的显示设备区域内放下理论上无数个界面元素,同时还可以很好地解决掉Chioce A带来的很多限制问题。但是,世界并非完美的,即便在无所不能的code世界也总有缺陷。在Chioce A的方案中因为每个界面元素都有自己唯一的独立区域,每当用户以触摸显示设备的方式与之交互时总会Touch到唯一的一个元素。但是在Chioce B中则不行了……因为界面元素之间的层叠关系,每当我们触摸时都有可能Touch到N个层叠的元素,怎么办呢?这时我们就必须引入一套机制,让系统有能力去处理并判断用户想要Touch哪个元素,So,事件机制油然而生。
当然,事件机制的出现并非因为触摸屏,而上述的两种界面显示思想也并非来自Android的射鸡师,自显示设备出现的那天起便已存在,这里仅作一个引子抛砖引玉而已。
既然Choice B是我们的选择,那么我们应该如何对事件进行投递呢?Suppose我们有如下界面元素构成:
注:这里暂不考虑什么Window、DecorView等等,我们尽量抽象思维不要让思维惯性地和code靠在一起令逻辑变得复杂。
如上图所示,G为我们的显示设备屏幕,A、C、E都在G中为同一层级,而B、D、F分别在A、C、E中,其层叠效果就如上图所示,此时,假设我们
Touch屏幕中的某一区域,如下图中的红色区域:
这时按照人的行为模式F元素是应该获取到该Touch事件的,也就是说当Touch在屏幕G上发生后,交由F元素:
G----->F
交由F处理后那么F就有责任告知系统本次事件我吃掉了。但是这样会有个问题,如果说我们想让F下方的E也响应本次的Touch事件呢?那么我们的传递方式就该是这样的一个模式:
G----->F----->E
也就是说E和F都能吃掉该次事件,如果位于F和E下方的DCBA也要吃呢?这就陷入了一个窘境,每次谁要吃我们是事先不知道的,有可能谁一时兴起要吃掉该次事件,如果按照我们当前的这种“置顶优先”的方式来传递事件肯定是不科学的,同时也不符合我们界面元素层叠结构之间的关系。那么好办了,我们从底层开始传递事件,还是拿上图的Touch点来说,当触摸区域在上图的红色位置时,我们可以让系统通过一系列的计算获知事件点所在区域,一旦发现事件点落在某个界面元素的范围内(上图中事件点在所有的元素范围内)那么我们就按其层叠顺序依次将事件出递出去,比如上图中,事件由屏幕G点出发途径:
G----->A----->B----->C----->D----->E----->F
这样一次事件就可以依次经过多个可能捕获它的元素,这样的传递方式有一个专业的称谓:隧道式传递。一旦某个元素想要吃掉该次事件那么我们只需通过一定的逻辑处理告知系统本次事件我吃掉了,你不需要再传递给别人了!这时,我们就该把这个信号层层递交返回给系统,这种递交的方式也有一个有趣的名字:冒泡机制。就像水里的鱼在某个位置吐泡,总会冒回水面的。而这种以链式结构传递的方式在设计模式中也有迹可循,具体的饭迎大家关注SM哥的SAOS开源组织发起的Android与设计模式系列下Aige出品的责任链模式。当然你也可以让一次事件无序传递甚至乱传,只不过不提倡而已。
在Android的实际处理中事件机制绝非上面我们说的那么简单,上面的分析仅仅是一个浓缩版的事件机制结构。本节仅为一个开端,作为详解事件机制的一个引子没有代码的涉及,本来在编写自定义系列的交互文章时发现肯定会涉及事件机制,如果一起写显得太臃肿,干脆分开来更好地阐述,本系列作为一个中间桥梁上连自定义系列,下接GUI框架剖析,So,敬请关注。