由于最近写了一个仿京东图片拖拽返回的控件,打算把事件分发的东西结合源码记录下来,由于之前没有记录打算这次记录起来印象更深刻,
之后会在写一篇,关于事件分发的升级版,支持库v21以后才有的 嵌套滚动机制( NestedScrollingChild, NestedScrollingParent)以及和behaive的关系
现在我住的地方是成都,以修桥的事情来说。
四川省 成都市要在高新区的中和镇修一座桥,来疏通中和的交通情况,其中3个角色,这里我们简单 划分为两个viewgroup和一个 View: 成都市-> 最外层viewgroup 高新区-> 第二层viewgroup 中和镇-> 在第二层下面的view
结构如下:
现在我们已经明确了任务就是修桥
- 成都市要把修桥任务告知于它的下级高新区
- 高新区要把修桥任务告知于它的下级中和镇
- 中和镇知道了自己这儿要修一座桥
现在有两种情况,中和镇可以选择修或者不修
修:
-
成都市把任务传递给高新区
-
高新区把任务传递给中和镇
-
中和镇觉得自己需要修一座桥
-
到此 此项任务传递完成,接下来中和镇就要自己处理修桥事项了
不修:
-
成都市把任务传递给高新区
-
高新区把任务传递给中和镇
-
中和镇很傲娇,它不修
-
此时中和镇把任务又传递了高新区说我不修
-
高新区得到反馈传递给了成都市
-
此时 整个任务传递结束,最后由成都市来处理这个任务了
此时还有另外一种情况,被高新区的贪官给私吞了,他觉得中和没必要修一座桥,倒是高新区需要一座桥,他就在高新区传递任务的时候做下手脚,把任务拦截下来了,不告诉中和镇
情况如下:
本来高新区准备把任务传递给中和镇的途中,这个时候贪官直接把下发文件拦下来了,这个时候这个任务就被高新区独吞了,下面的根本就不知道了,那么整个任务就结束咯,那么此次任务知晓者只有 成都市和高新区了
注意以上的任务对应的是一个事件,比如ACTION_DOWN算一个任务,ACTION_MOVE算一个任务,ACTION_UP算一个任务,每发布一个任务(相当于触发了一个事件),就会像上面那样重走上面的所有传递流程。
比如像上面我们可以想象,ACTION_DOWN就相当于发布修桥通知任务,ACTION_MOVE相当于签署修桥协议任务,ACTION_UP相当于动工任务
上面每一个任务都会经过传递和处理只要有一个不传递或者被处理了,其他的都不会接收到此项任务。
假如有这样一种情况,都收到了修桥通知任务(ACTION_DOWN),但是修桥协议任务(ACTION_MOVE)被高新区不传递了,那么后面的中和镇也不会收到桥协议任务(ACTION_MOVE),如果动工任务(ACTION_UP)传递了也不拦截,那么中和镇还是可以收到动工任务(ACTION_UP)
只要我们明确两个点,一个事件和一串事件序列:
-
一个事件它可以是ACTION_DOWN或ACTION_MOVE或ACTION_UP,每一个事件都会经过一系列的传递和是否被拦截以及会不会被处理。
-
一个完整的事件序列,它是由ACTION_DOWN,ACTION_MOVE,ACTION_UP组成的,手指的按下和移动和抬起算一个完整的事件序列,像上面的3个修桥任务一样只有3个任务完整传递了修桥才会开始
那我们在对效果处理的时候,我们是针对某一个事件做的,比如我们会在down事件的时候做一些事情,move的时候做一些事情等,我们有权决定当前事件是否给下级view
好了修桥任务完了,如果理解上面的东西的话,那么我觉得你至少已经把事件分发理解的八九不离十了,接下来只要结合源码或者写几个例子跟着上面的逻辑跑几乎在理解上面已经没有多大的问题了,置于源码逻辑怎么处理,可以自己去结合源码读读看看。
** 事件分发的几个重要方法**
- ViewGroup
** dispatchTouchEvent ->传递事件**
** onInterceptTouchEvent -> 拦截事件**
** onTouchEvent -> 处理时间**
-
View
dispatchTouchEvent ->传递事件
onTouchEvent -> 处理时间
这里就不贴源码,只说这些方法的返回值对应的处理
以上面的图片布局结构为例子
- ViewGroup的dispatchTouchEvent()
返回是surpe.dispatchTouchEvent()时,事件会正常传递给下一级view的dispatchTouchEvent方法
返回是true时,表示此次事件不在传递了,以后的这次事件都会到当前view的dispatchTouchEvent()方法就结束掉
返回时是false时,表示此次事件不在传递了,也表示我不关心
以上都是单个事件的情况,下面以整个事件序列的返回情况出发
当DWON,MOVE,UP事件都返回surpe.dispatchTouchEvent()时,表示事件正常传递下去,所有view的dispatchTouchEvent都可以接收到
当DWON返回surpe.dispatchTouchEvent(),MOVE,UP返回true,表示只有DOWN事件才会传递给下面的view的dispatchTouchEvent,其余的MOVE,UP就不传递了,下面的view收不到这两个事件
当DWON返回surpe.dispatchTouchEvent(),MOVE返回true,UP返回surpe.dispatchTouchEvent(),表示只有DOWN事件和UP事件才会传递给下面的view的dispatchTouchEvent,MOVE就不传递了,下面的view收不到这个事件
其余情况都是一样,请结合上面写例子体会
- ViewGroup的onInterceptTouchEvent()
返回是surpe.onInterceptTouchEvent()时,事件默认不拦截,会传递给下面的view的onInterceptTouchEvent
返回是true时,表示此次事件被拦截,直接交给当前view的处理者onTouchEvent(),其他下面的view收不到此次事件
返回时是false时,事件不拦截,继续给下面的view决定
以上都是单个事件的情况,下面以整个事件序列的返回情况出发
当DWON,MOVE,UP事件都返回surpe.onInterceptTouchEvent()时,表示事件正常传递下去,所有view的onInterceptTouchEvent都可以接收到
当DWON返回surpe.onInterceptTouchEvent(),MOVE,UP返回true,表示只有DOWN事件才会传递给下面的view的onInterceptTouchEvent,其余的MOVE,UP就不传递了,下面的view收不到这两个事件
当DWON返回surpe.onInterceptTouchEvent(),MOVE返回true,UP返回surpe.onInterceptTouchEvent(),表示只有DOWN事件和UP事件才会传递给下面的view的onInterceptTouchEvent,MOVE就不传递了,下面的view收不到这个事件
- View的dispatchTouchEvent()
view的dispatchTouchEvent代码内部调用了onTouchEvent(),一般不重写dispatchTouchEvent的情况下,那么dispatchTouchEvent的返回值是由onTouchEvent()方法返回值决定的,如果view设置了OnTouchListener,那么会直接调用OnTouchListener的回调,不会在调用了onTouchEvent了
如果一个view是可以点击的,那么这个view的onTouchEvent()会返回true,默认就要处理这个事件,事件到此就结束了
长按事件,点击事件都会在onTouchEvent方法里处理
- ViewGroup的dispatchTouchEvent()代码
首先如果第一次down事件会清除触摸目标和状态,且onInterceptTouchEvent 方法是对down无效了,因为只要满足下面代码一个条件就才会进去,第一次down所以直接进不了这个方法体
那么拦截标记就是 false,会进入到下面把事件传递给下一级view,然后看是否要处理,如果都不处理,最终会到最上层view,主要是方法是
然后第二次进来触摸目标的值就不是null,所以会调用onInterceptTouchEvent是否要拦截,如果要拦截默认给当前view处理