Q1:分发的事件被view消耗了,后续事件是如何快速找到这个view的?
ViewGroup不拦截事件又是如何将事件分发给子View?
带着问题重学Android事件分发 - 掘金
我们之前经常背的八股文:
当你手指触摸到屏幕这时候ViewGroup首先接收到的是一个down事件,如果不拦截则会将事件分发到子view中,首先会遍历循环所有的子控件调用 dispatchTransformedTouchEvent
函数,也就是子控件的 dispatchTouchEvent
方法,由子控件继续执行事件的分发,这时候如果 child 消费了事件 dispatchTouchEvent
会返回true,接着会执行 addTouchTarget
函数和将 alreadyDispatchedToNewTouchTarget
标记设置为 true,alreadyDispatchedToNewTouchTarget
标记表示是否有子控件消费了这个事件,newTouchTarget
默认为 null 也就是在有 child 消费事件后才会创建一个节点。
在 addTouchTarget
函数中首先将 child 封装成一个 target 对象,TouchTarget 是一个单链表的数据结构在这里 ViewGroup 中的 mFirstTouchTarget
指向“封装child的target”,从这里可以看出如果有子控件消费了事件那么 mFirstTouchTarget 必然不为 null
TouchTarget(面试)
ViewGroup 里 TouchTarget
对象可以看做在事件分发序列中第一个消费了事件的控件对象的封装,除了记录消费事件的 View 对象还具备在 move 事件到来时快速定位具体的 子View 来处理事件,当一个子View消费了事件时 dispatchTransformedTouchEvent
返回 true ,接着调用 addTouchTarget
函数新建一个 TouchTarget
节点
Q2:OnTouchListener、onTouchEvent、OnClickListener的优先级
1、看下view的dispatchTouchEvent源码:
public boolean dispatchTouchEvent(MotionEvent event) {
//...
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
//...
return result;
}
可以非常清楚看到,先执行的是onTouchListener的onTouch方法,如果返回true则代表事件被消费掉,则不会往下走到onTouchEvent中;相反,如果没有消费则才往下走到onTouchEvent中。
2、接着看view的onTouchEvent方法:
可以很清楚看到,OnTouchListener是执行在onTouchEvent之前的,OnTouchListener优先级高于onTouchEvent,如果程序注册了onClickListener,则会执行onClickListener的onClick方法
总结如下:
OnTouchListener --> onTouchEvent --> OnLongClickListener/OnClickListener
1、view在执行dispatchTouchEvent的时候先要去判断该view是否有touchListener和执行TouchListener的onTouch方法的返回值。
2、TouchListener的onTouch方法返回false,事件继续传递在dispatch方法中调用了同级的onTouchEvent方法
3、然后在onTouchEvent方法中的UP事件中判断是否注册有onClickListener,有就执行onclick方法。
android中的事件传递和处理机制 - fuly - 博客园
那天有人问我,Android 的事件到底是怎么来的?