前言
本文粗略解析下事件分发机制,后续会分析下源码,希望能够帮助到大家解惑一二。
从一个例子说起
在实践中,大多数需要了解事件分发机制的可能就是滑动冲突了,需要了解什么时候到底谁应该处理这个事件,先来看个例子,当我们点击区域View
默认不处理事件
假如ViewGroup和View都默认不处理事件,不复写对应函数
- DOWN事件传递到View的onTouchEvent,返回false,表示View本身不关心这次事件
- DOWN事件传递到ViewGroup的onTouchEvent,返回false,表示ViewGroup本身不关心这次事件
- 最终事件回溯到Activity中,由最上层处理事件
由于onTouchEvent中返回了false,同时也就代表着不关心本次事件(我不关心这次手势,不要来找我了),后续的MOVE,UP事件也不会传递进来
View处理事件
假设View是需要处理事件,比如是Button之类的View,默认是可点击的
- DOWN事件传递到View的onTouchEvent,返回True,处理自己的逻辑
- 因为View中的onTouchEvent返回了True,表示有人愿意处理了这个事件,那么后续事件将不会传递到ViewGroup以及上层的onTouchEvent中了,后续的事件将由View本身进行处理
注意上述的前提是ViewGroup不会对此事件进行拦截,因为在DOWN事件传递到View的ontouchEvent之前,ViewGroup是有一次拦截机会的,如果ViewGroup的onInterceptTouchEvent返回false,则不拦截,但是注意下一次的MOVE、UP等事件还是会走ViewGroup拦截的判断,可根据逻辑进行拦截处理,如果没有拦截,但是View的ontouchEvent返回了false,也就表示下面没有View是愿意处理这个事件的,那么这个烫手的山芋(事件)还是会回到ViewGroup的ontouchEvent中
同时可见DOWN事件的返回值,其实就是表示着View本身对本次事件处理的意愿如何,True则代表着愿意处理该事件,false则代表不关心本次事件
ViewGroup拦截事件
假如ViewGroup在DOWN事件中,拦截了事件onInterceptTouchEvent返回了true,那么此时事件直接转到ViewGroup的ontouchEvent中,后续的MOVE、UP事件也会交给ViewGroup处理,View是没有机会处理到事件的,即使此时调用requestDisallowInterceptTouchEvent也是无效的
假如ViewGroup在DOWN事件中没有拦截事件,但是在MOVE中却对事件进行了拦截处理,比如类似如ScrollView一样,是可以对其中的View进行点击处理的,但是在滑动时,ScrollView需要处理自己的逻辑,这时候就在MOVE中拦截了事件
- DOWN事件传递在ViewGroup中,但此时并不想拦截,onInterceptTouchEvent返回false,事件传递到View的ontouchEvent中,消费掉返回True
- MOVE事件传递到ViewGroup中,此时ViewGroup需要对事件进行拦截处理,onInterceptTouchEvent返回True,但是View还苦巴巴的等着事件呢,因为之前在ontouchEvent中返回了True,那么此时这个MOVE事件将会被系统变成一个CANCEL事件,这个CANCEL事件将会传递给VIEW的onTouchEvent方法,告诉你,别等了,你的事件被取消了
- 当MOVE事件或者UP事件再次进入ViewGroup的时候,注意此时由于之前已经拦截了事件,此次事件并不会走onInterceptTouchEvent的判断,可以理解为onInterceptTouchEvent一旦返回了True,那么后续的一系列事件就默认已经被拦截处理了,不会再去判断!而是会直接传递给ViewGroup的onTouchEvent方法去处理了
- 此时View就不会受到任何的事件了
总结
-
同一时间序列事件是指以down事件开始,中间含有数量不定的move事件,最终以up事件结束。
-
各个View的onTouchEvent方法对DOWN事件的处理,代表了该View对以此DOWN开始的整个手势事件的处理意愿,True代表需要处理此次事件,false则表示不关心,事件会回传到上层View的ontouchEvent中
-
如果ViewGroup一旦决定拦截一个事件,也就是onInterceptTouchEvent返回True,那么后续的同一时间序列事件将会被默认拦截,不会再调用onInterceptTouchEvent方法,后续事件会交给ontouchEvent进行处理
-
事件一旦交给一个View处理,那么它就必须消耗掉,否则同一事件序列中剩下的事件就不再交给他来处理了,如果ontouchEvent中返回false,那么后续事件不会再传递进来
-
如果ViewGroup拦截了一个半路的事件(比如,MOVE),这个事件将会被系统变成一个CANCEL事件,并传递给之前处理该手势(gesture)的子View,而且不会再传递(无论是被拦截的MOVE还是系统生成的CANCEL)给ViewGroup的onTouchEvent方法。只有再到来的事件才会传递到ViewGroup的onTouchEvent方法中。