一丶 一个完整的事件传递机制 必定包含三种事件: 由一个 down 事件、 多 个 move 事件,一个 up 事件组成(还有一个cancel事件,该事件是在被上层拦截时触发)。
手指落下(ACTION_DOWN) -> 移动(ACTION_MOVE) -> 离开(ACTION_UP)
二丶Touch一般的传递流程:Activity------>window(唯一实现类是PhoneWindow)------>顶级View(DecorView)------>ViewGroup------>View ;(可概括为一句话:责任链模式,事件层层传递,直到被消费)
三丶监听Touch事件的两种方式:setTouchListener和直接重写三个方法:dispatchToucEvent, onInterceptTouchEvent, onTouchEvent.
1.setTouchListener:
此方法监听的优先级比较高,如果在onTouchListener的onTouch方法里面执行了return true,那么说明消费了该事件 ,而OnTouchEvent是接收不到该事件的,因此在onClickListener里面的Onclick方法是执行不到的,因为Onclick是在OnTouchEvent种被调用的,因此Touch事件走不到OnTouchEvent事件的话,Onclick方法是不会被执行的.
2,dispatchToucEvent:
为什么ViewGroup有dispatchToucEvent,而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一层一层传递的,最终传递给 View,ViewGroup 要比它的 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 进行了拦截。