View的事件分发机制
View的事件分发机制主要分为三点
,第一点是Activity将点击事件分发给ViewGroup
第二点是ViewGroup将事件自己处理或者分发给子View
第三点便是子View自行处理,或者子View处理不了转交给ViewGroup
现在依次来看
Activity对点击事件的分发过程
Activity的dispatchTouchEvent主要是Activity内部的Window来完成,Window将判断
getWindow().superDispatchTouchEvent(ev)
是否为false,如果为false,则由Activity自己处理这个点击事件,即Activity调用onTouchEvent,而如果返回值为true
,表示Window已经消费了该点击事件,不再继续向下传递,由PhoneWindow(PhoneWindow是Activity的实现类)处理后续的操作。
PhoneWindow将事件传递给DecorView,最后又通过DecorView传递给ViewGroup
ViewGroup对事件的分发过程
ViewGroup对事件的分发过程有3种可能性,一种是ViewGroup自己拦截的点击事件,然后自己处理了,还有一种是ViewGroup没有拦截事件,但是传递给了子View,但是子view没办法自己处理,最后又交给了ViewGroup处理。最后一种是ViewGroup没有拦截事件,传给子View,子View处理了
ViewGroup拦截事件
ViewGroup如果onInterceptEvent返回了true,则表示该事件被ViewGroup拦截住了,先判断有没有onTouchListener,如果有这个则调用里面的onTouch,没有则判断有没有onTouchEvent,如果里面设置了onClickListener则调用这个方法。
如果返回的是false,则传递给子View
但是onInterceptEvent并不是每次都会调用。
它有一个FLAG_DISALLOWINTERCEPT标记,这个标记会进行判断:
1.如果是一个ACTION_DOWN行为的话,它会在onterceptTouchEvent()里面进行判断是否进行拦截,如果不进行拦截的话且如果子View消费了该事件,则FLAG_DISALLOWINTERCEPT标记对于同一个事件的ACTION_MOVE,ACTION_UP等等都不再进行拦截了。
2.如果不进行拦截的话,且子View没有消耗该事件,ViewGroup仍然有可能拦截同一个事件序列内的其他动作。
3.如果进行拦截了当前事件,并不会将该事件传递给子View进行处理,同时也不会再接收该事件的后续动作。这意味着同一个事件序列内的其他动作**(如ACTION_MOVE、ACTION_UP等)**不会被该ViewGroup拦截。
所以ViewGroup的onInterceptEvent并不会一直调用
我们再看看ViewGroup把事件传递给子View吧
ViewGroup的传递
ViewGroup会对所有的子View进行一次遍历,并且对它们进行判断,
对于子View有以下几种判断:
1.如果子View的位置不在点击事件的坐标范围内或者子View不接收指针事件(如设置了不可点击或不可触摸),则跳过该子View。
2.如果子View已经在处理点击事件,即已经接收到ACTION_DOWN事件并在处理中,直接将触摸事件传递给该子View,并将点击事件标记为已经分发给子View处理。
3.如果子View还未处理点击事件,则调用dispatchTouchEvent()方法将点击事件传递给子View进行处理。
4.如果所有的子View都无法处理点击事件,或者ViewGroup没有子View,则ViewGroup会自己处理该点击事件。
总结起来,顶级View对点击事件的分发过程包括拦截判断、子View的遍历和分发。如果ViewGroup拦截了ACTION_DOWN事件,它会处理该事件;如果没有拦截,它会将事件传递给合适的子View进行处理,如果没有合适的子View,则自己处理该事件
View对点击事件的处理
这里的View不包含ViewGroup,因为ViewGroup的我们刚才已经讲过了
View没办法继续向下传递了,所以它就两种情况:
1.自己可以处理,然后把这个事件消耗了
2.自己没办法处理,然后转交给ViewGroup处理
就直接说View自己处理的
首先,View会判断是否存在onTouchEvent()、OnTouchListener、OnClickListener等相关事件处理方法或监听器。
如果存在OnTouchListener,则会调用OnTouchListener的onTouch()方法,如果onTouch()方法返回true,表示该触摸事件已被消费,不再继续传递给其他处理方法或监听器。
如果存在OnClickListener,并且点击事件满足点击条件(如点击类型、点击位置等),则会调用OnClickListener的onClick()方法,并且事件被视为已经消费。
如果点击事件还未被消费,则会判断View的clickable和longClickable属性是否为true。如果clickable为true,则视为该View可以接收点击事件并消费;如果longClickable为true,则视为该View可以接收长按事件并消费。
如果上述条件均不满足,或者View已经消费了点击事件,则事件会继续向上层父View传递,由父View或上层ViewGroup处理。
,则视为该View可以接收长按事件并消费。
如果上述条件均不满足,或者View已经消费了点击事件,则事件会继续向上层父View传递,由父View或上层ViewGroup处理。