浅谈Android中的事件分发机制

又有一段时间没有更新博客了,每次简书上的朋友给我之前的文章点赞或者评论,手机的推送都会响起,心中总有种说不出的感觉,挺难受的。发现真正投入工作后,没有像在学校里有那么一整块完整的时间可以让自己去自我填充,整理一些知识点。

今天打算来讲下关于Android的事件分发机制,Android的事件分发在平时工作中零零散散的也用到许多,特别是在自定义View和多层嵌套布局的情况下。

要了解Android的事件分发机制,首先我们要从最常见的一个用户行为说起——触摸屏幕,当我们触摸手机屏幕时发生了什么?
这里通常会产生2-3个事件:
事件一:按下
事件二:滑动(如果在点击屏幕的时候,用户手抖了一下)
事件三:抬起
Android为这三个事件(触摸事件)封装了一个完整的类——MotionEvent,当你对任意的View进行onTouchEvent方法重写的时候,就可以发现方法参数里有个MotionEvent类,在MotionEvent类中我们可以得到许多东西,比如获取当前触碰点的x,y坐标,获取当前触碰的动作action(按下,滑动,抬起),我们可以根据这些数据的组合来确定用户的手指行为,从而可以实现较为复杂的逻辑动作。

那么问题来了,在一个嵌套界面中,比如一个ViewGroup里嵌套了另一个ViewGroup,在另一个ViewGroup里再嵌套一个View,当我触摸他们重叠的地方时,系统是如果识别当前的触摸位置的?
如下图:


布局讲解图

这里的蓝色和红色分别代表ViewGroupA和ViewGroupB,这里的绿色代表View,它们存在一个相对布局里,一个叠着一个,当我们触摸绿色区域的时候,发生了什么事情?

**这里需要引出3个方法: **
1、dispatchTouchEvent(ViewGroup和View均有):用来进行事件的分发,如果触摸事件可以传递给当前的View,那么这个方法一定会被调用。

2、onInterceptTouchEvent(View没有):在dispatchTouchEvent方法的内部调用,用于判断该事件是否被拦截。

3、onTouchEvent(ViewGroup和View均有):在dispatchTouchEvent方法的内部调用,用于判断该事件是否被消费。

现在我们先来理一下这三者之间的关系:
我们把ViewGroupA比作老板,ViewGroupB比作经理,View比作员工。
当有任务的时候,通常是层级指派关系(老板-->经理-->员工),当完成任务的时候,任务的提交也是层级传递(员工-->经理-->老板)
老板,经理,员工都是独立的个体,他们都有自己对应的权限,就当前这个任务而言,老板如果觉得简单,他可以选择自己解决掉,但是如果老板觉得太困难了,他可以把任务下发给经理解决,此时经理也是有对应的权限,他可以选择自己解决,也可以选择将任务下发,给下一级的员工解决,那员工呢?当然就是最苦逼的那个了,因为他最小,没有继续下发指派的人了,他只能自己干了。

这里引出了一个知识点:
View(员工)比ViewGroup(老板,经理)少了一层拦截权限,也就是拦截方法onInterceptTouchEvent,这一点结论我们可以在源码中得到。聪明的你,应该可以感觉到,任务是否往下指派就取决于onInterceptTouchEvent这个方法,顾名思义“是否拦截”。而任务的处理在于各自的onTouchEvent方法里。

这里需要知道两点:
1、事件的传递(上往下):onInterceptTouchEvent拦截方法有个布尔类型的返回值,这里可以理解成是否拦截,如果为true,拦截,如果false,不拦截。

2、事件的消费(下往上):onTouchEvent事件处理方法里也有个布尔值类型的返回值,如果当前事件被处理(消费)了,那么他返回true,如果没有被消费需要传递给下一级处理,那么它返回false。

我们再来完整分析下任务与人物之间的关系,当一个任务下发的时候:
完全不阻拦的方式:老板拿到任务分发给经理,经理拿到任务分发给员工,员工处理。员工处理完任务提交给经理,经理审核完任务,提交给老板。
阻拦方式:老板拿到任务可以选择处理或者不处理,如果老板选择了把任务拦截下来自己处理了,也就是在ViewGroupA中的onInterceptTouchEvent返回了true(拦截),然后在自身的onTouchEvent里处理事件,反之下发任务给经理,经理同上,最后如果员工拿到了任务,就不会有是否拦截的说法,它只能在onTouchEvent直接处理任务。

接下来我们来结合代码验证下我们刚才说的场景吧,3个类,ViewGroupA,ViewGroupB,View,分别重写dispatchTouchEvent,onInterceptTouchEvent(View除外),onTouchEvent,然后在方法里打印Log日志,布局为最上面那张三色图。

自定义ViewGroup

现在我们点击下View区域,看下控制台显示的情况:


控制台打印

哈哈,和我们最初预想的结果是一样的,老板(ViewGroupA)接收到任务(dispatchTouchEvent),然后执行了是否拦截的方法(onInterceptTouchEvent),不拦截把任务传递给经理(ViewGroupB),然后依旧一样执行了是否拦截的方法(onInterceptTouchEvent),经理不拦截再将任务交给员工(View)处理,员工接收任务没办法分配,只能自己处理(onTouchEvent)了,处理完毕上报经理,经理再上报老板。

当然员工也有不乐意的一天,天天累死累活的,今天劳资不干了,在处理onTouchEvent返回true,看下会发生什么情况。

控制台打印

结果也和我们预想的一样,员工在onTouchEvent方法里返回了true,消费了该事件,也就没有事件再往上传递了。
由此可推,如果员工在onTouchEvent方法中返回的是false,那么事件就会继续向上传递,此时ViewGroupB的onTouchEvent方法中返回true,那么就只会到这层,不会再传递到ViewGroupA的onTouchEvent中去。

这里总结下几个结论:
1、一个触摸事件通常是由手指按下的那一刻起,中间经历了n个移动,最后以手指的上抬动作为止。
2、一个触摸事件通常只能被一个View所拦截(onInterceptTouchEvent)且被消耗(onTouchEvent),因为一旦onTouchEvent返回true,事件就不再传递了。
3、ViewGroup默认不拦截任何事件,这点我们可以在源码中ViewGroup的onInterceptTouchEvent方法里得知,因为他默认返回false。
4、View没有onInterceptTouchEvent方法,事件一旦传递给它,它的onTouchEvent方法就会被调用。
5、View默认的onTouchEvent都会被消耗,默认返回true,除了本身不可点击的View之外,比如TextView,ImageView等。
6、View的enable属性不影响onTouchEvent默认的返回值,就算它是disable状态,只要他的clickable或者longClickable有一个为true,那么它的返回值默认就为true。
7、事件的传递是由外到内的,事件总是先传递给父元素再传递给子元素,可以通过requestDisallowInterceptTouchEvent方法在子元素中干预父元素的事件分发,按下事件除外。
8、当ViewGroup的onInterceptTouch返回为true的时候,那么在这个事件序列中剩下的触摸事件都会由这个ViewGroup的onTouchEvent来处理,就不会再向下传递给ViewGroup或者View了,如果onInterceptTouch返回为false,则接下去的触摸事件会继续向下传递。
9、如果对View或者ViewGroup设置了OnTouchListener监听,则会重写onTouch方法,这里的onTouch优先级会高于onTouchEvent,如果onTouch返回为true,那么事件就会被消费,不会再传递给onTouchEvent了。

好了,今天先写到这吧,了解上面所说的知识点,应付日常工作所遇到的一些问题基本够用了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容