自定义View事件的分发及传递机制

一丶  一个完整的事件传递机制  必定包含三种事件: 由一个 down 事件、 多 个 move 事件,一个 up 事件组成(还有一个cancel事件,该事件是在被上层拦截时触发)。

手指落下(ACTION_DOWN) -> 移动(ACTION_MOVE) -> 离开(ACTION_UP)  

二丶Touch一般的传递流程:Activity------>window(唯一实现类是PhoneWindow)------>顶级View(DecorView)------>ViewGroup------>View    ;(可概括为一句话:责任链模式,事件层层传递,直到被消费)



三丶监听Touch事件的两种方式:setTouchListener和直接重写三个方法:dispatchToucEventonInterceptTouchEvent,  onTouchEvent.


1.setTouchListener:

此方法监听的优先级比较高,如果在onTouchListener的onTouch方法里面执行了return true,那么说明消费了该事件 ,而OnTouchEvent是接收不到该事件的,因此在onClickListener里面的Onclick方法是执行不到的,因为Onclick是在OnTouchEvent种被调用的,因此Touch事件走不到OnTouchEvent事件的话,Onclick方法是不会被执行的.

2,dispatchToucEvent:

为什么ViewGroupdispatchToucEvent,View 也有dispatchToucEvent,是因为View 可以注册很多事件监听器,例如:单击事件(onClick)、长按事件(onLongClick)、触摸事件(onTouch),并且View自身也有 onTouchEvent 方法,那么问题来了,这么多与事件相关的方法应该由谁管理?就是dispatchTouchEvent.()

那么,这么多的事件,他们的事件调度顺序是怎样的呢?

从源码的角度去分析: 

从设计的角度去分析:

1).单击事件(onClickListener) 需要两个两个事件(ACTION_DOWN 和 ACTION_UP )才能触发,如果先分配给onClick判断,等它判断完,用户手指已经离开屏幕了,定然造成 View 无法响应其他事件,应该最后调用。(最后)

2).长按事件(onLongClickListener) 同理,也是需要长时间等待才能出结果,肯定不能排到前面,但因为不需要ACTION_UP,应该排在 onClick 前面。(onLongClickListener > onClickListener)

3).触摸事件(onTouchListener) 如果用户注册了触摸事件,说明用户要自己处理触摸事件了,这个应该排在最前面。(最前)

4).View自身处理(onTouchEvent) 提供了一种默认的处理方式,如果用户已经处理好了,也就不需要了,所以应该排在 onTouchListener 后面。(onTouchListener > onTouchEvent)


由上可以推导出来:

事件的调度顺序应该是onTouchListener > onTouchEvent > onLongClickListener > onClickListener


举个栗子:


LinearLayout这里设置了点击一个事件,然后在执行的时候你会发现怎么点击都不会接收到消息,这是因为View这里设置了clickble的属性为true.也就是这个事件被孩子吃掉了.那么父亲是不会再走TouchEvent的

3.onInterceptTouchEvent:

这个是ViewGroup专有的,该事件在ViewGroup一层一层传递的,最终传递给 ViewViewGroup 要比它的 ChildView 先拿到事件,并且有权决定是否告诉要告诉 ChildView

一般情况下,该事件在ViewGroup中是这样分发的:

1.判断自身是否需要(询问 onInterceptTouchEvent 是否拦截),如果需要,调用自己的 onTouchEvent。

2.自身不需要或者不确定,则询问 ChildView ,一般来说是调用手指触摸位置的 ChildView。

3.如果子 ChildView 不需要则调用自身的 onTouchEvent。

ViewGroup通过遍历ChildView,确定手指点在哪个ChildView的区域内,然后将事件发放到该ChildView,而当ChildView存在覆盖的情况时,ViewGroup会将事件分发到最上层的ChildView上(一般后加载的CHildView是会覆盖前面加载了的,所以最上层的是最后加载的)



当手指点击有重叠区域时,分如下几种情况:

只有 View1 可点击时,事件将会分配给 View1,即使被 View2 遮挡,这一部分仍是 View1 的可点击区域。

只有 View2 可点击时,事件将会分配给 View2。

View1 和 View2 均可点击时,事件会分配给后加载的 View2,View2 将事件消费掉,View1接收不到事件。

注意:

上面说的是可点击,可点击包括很多种情况,只要你给View注册了onClickListener、onLongClickListener、OnContextClickListener其中的任何一个监听器或者设置了android:clickable=”true”就代表这个 View 是可点击的。

另外,某些 View 默认就是可点击的,例如,Button,CheckBox 等。

给 View 注册 OnTouchListener 不会影响 View 的可点击状态。即使给 View 注册 OnTouchListener ,只要不返回 true 就不会消费事件

3. ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),哪个会执行?

事件优先给 ChildView,会被 ChildView消费掉,ViewGroup 不会响应。

4. 所有事件都应该被同一 View 消费

在上面的例子中我们分析后可以了解到,同一次点击事件只能被一个 View 消费,主要是为了防止事件响应混乱,如果再一次完整的事件中分别将不同的事件分配给了不同的 View 容易造成事件响应混乱。

View 中 onClick 事件需要同时接收到 ACTION_DOWN 和 ACTION_UP 才能触发,如果分配给了不同的 View,那么 onClick 将无法被正确触发。

安卓为了保证所有的事件都是被一个 View 消费的,对第一次的事件( ACTION_DOWN )进行了特殊判断,View 只有消费了 ACTION_DOWN 事件,才能接收到后续的事件(可点击控件会默认消费所有事件),并且会将后续所有事件传递过来,不会再传递给其他 View,除非上层 View 进行了拦截。

如果上层 View 拦截了当前正在处理的事件,会收到一个 ACTION_CANCEL,表示当前事件已经结束,后续事件不会再传递过来。(详细细节可以查看源码~~~~)

核心要点(重点!!!!)

事件分发原理: 责任链模式,事件层层传递,直到被消费。

View 的dispatchTouchEvent主要用于调度自身的监听器和 onTouchEvent。

View的事件的调度顺序是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener 。

不论 View 自身是否注册点击事件,只要 View 是可点击的就会消费事件。

事件是否被消费由返回值决定,true 表示消费,false 表示不消费,与是否使用了事件无关。

ViewGroup 中可能有多个 ChildView 时,将事件分配给包含点击位置的 ChildView。

ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),由 ChildView 消费。

一次触摸流程中产生事件应被同一 View 消费,全部接收或者全部拒绝。

只要接受 ACTION_DOWN 就意味着接受所有的事件,拒绝 ACTION_DOWN 则不会收到后续内容。

如果当前正在处理的事件被上层 View 拦截,会收到一个 ACTION_CANCEL,后续事件不会再传递过来


最后附一张示意图:

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

推荐阅读更多精彩内容