click事件生成和attachInfo传递
click事件生成(onClick调用)
在Android开发中经常用到click事件监听,但其实click事件并不是地方传上来的事件,底层上报事件都是touch事件,而click事件其实是根据touch事件生成的,或者说click事件并不是一个纯粹的事件
在View的onTouchEvent方法中,在收到MotionEvent.ACTION_DOWN事件的时候有如下逻辑
这里截取了部分主要源码,这里其实主要设置了下flag,然后主要是调用了checkForLongClick(走if分支时CheckForTap也会调用checkForLongClick方法)该方法主要是发送一个延时事件,如果到时该延时事件没有取消,则会执行该事件,这就是LongClick事件的生成,稍后再总结长按事件
在收到MotionEvent.ACTION_UP事件的时候
这里截取了部分主要源码,前面removeLongPressCallback方法会移除在down事件时发送的长按事件的延时任务,而mPerformClick任务则是执行click事件
click事件
根据前面的分析,click事件主要代码如上,其调用有两种,首先post一个PerformClick任务,如果post返回false则调用performClickInternal方法,其最后的逻辑都是一致的,这里先看下PerformClick任务
显然PerformClick任务中就是调用performClickInternal方法
继续走读代码,performClickInternal主要时调用performClick方法
到performClick方法就很明朗了,这里有获取onClick监听,如果有添加onClick监听器,则这里调用其onClick方法
长按事件
长按事件和click事件相似,不过长按事件实际是在down时生成的(延时任务),不过如果up时还没执行则会取消长按事件(还有些如cancel事件等场景这里不具体讨论,只是大概介绍下流程)
根据前面介绍的情况,这里走读下一般场景下调用,如上,走checkForLongClick方法
显然这里是postDelayed一个CheckForLongPress任务,查看其run方法:
显然,CheckForLongPress任务触发时会调用performLongClick方法,继续走读代码
到这里就到到具体事件回调的地方了,在performLongClickInternal方法中,如果有设置长按事件监听器,这里即会调用其onLongClick方法
post和postDelayed
前面分析中和View代码中有多处post和postDelayed方法调用,但其并不是直接有个handle去调用,View自己有实现post和postDelayed方法
如上,其post和postDelayed方法结构相似,其都是调用的mAttachInfo的mHandler对象的post和postDelayed方法(getRunQueue的相关调用看代码只在dispatchAttachedToWindow方法中会执行,正常来说一般在界面显示的时候就会走dispatchAttachedToWindow方法,后面除非移除控件再次添加,不然基本不会 重复调用,所以getRunQueue的相关调用一般情况下基本不会走到)
mAttachInfo在下面会具体分析,其mHandler是ViewRootImpl中的ViewRootHandler内部类的一个对象(ViewRootHandler继承Handler)
attachInfo传递
View的mAttachInfo最开始是在ViewRootImpl中创建的,然后在第一次绘制的时候分发到控件树的根节点,然后依次向子节点分发
如上,在ViewRootImpl的构造方法里创建了View.AttachInfo的对象mAttachInfo
然后在ViewRootImpl的performTraversals方法中mFirst条件代码中有调用host.dispatchAttachedToWindow方法,如下
这里host即是一般控件树的根节点
然后看下View和ViewGroup的dispatchAttachedToWindow方法逻辑
在View的dispatchAttachedToWindow中首先会给当前控件的mAttachInfo变量赋值,然后有些回调等,如onAttachedToWindow调用
在ViewGroup的dispatchAttachedToWindow主要是将attachInfo传递到控件树的子节点
这里有super调用,即会走View的dispatchAttachedToWindow方法,这样attachInfo就赋值给了当前控件
然后有对mChildren和mTransientViews分别进行遍历调用,前者是一般的控件树传递,后者是临时控件的传递,其逻辑相似(临时控件用的场景比较少,而且这里逻辑相似不单独分析)
这里mChildren就是控件树中的当前控件的子节点控件数组,这里对子节点控件分别调用dispatchAttachedToWindow方法,即会将attachInfo传递给了其子控件,就这样,attachInfo就从控件树的根节点一级一级的传递到控件树所有控件
不要在onAttachedToWindow中调用bringToFront方法
之前工作有遇到个控件点击无响应的问题,最后分析是控件的mAttachInfo未赋值,而其根因是在其无响应控件同级控件的onAttachedToWindow中有调用bringToFront方法,在atttachInfo分发时改变了控件的顺序,导致有的控件并未分发到,从而导致了问题
这里简单介绍下
在前面attachInfo分发中(ViewGroup. dispatchAttachedToWindow)已经了解到其是遍历mChildren调用其子元素的dispatchAttachedToWindow方法
然后我们先看下bringToFront方法(View),其会调用其父节点控件的bringChildToFront方法
其会调用其父节点控件的bringChildToFront方法(ViewGroup)
bringChildToFront方法前面主要就是操作mChildren数组,将对应的控件先从mChildren移除,然后添加到mChildren末尾
然后回到这个问题的过程,
这里画了个简易流程图来说明,这里假设假设父控件是当前分发attachInfo的控件,其有两个子控件,子控件1在onAttachToWindow方法中调用了bringToFront方法
然后分析其分发过程:
- 将attachInfo赋值给父控件
- 遍历其子控件,先调用其第一个子控件,这里即子控件1的dispatchAttachedToWindow方法,然后会将attachInfo赋值给子控件1,然后回调其onAttachedToWindow方法,然后其调用了bringToFront方法,即会将子控件1挪到其父控件的子控件列表的末尾,即变成右图所示
- 继续遍历,这时调用父控件的第二个子控件,这里根据上图右图所示仍是子控件1
从上面分发过程可知,这个过程中子控件2其实并没有遍历到,也就没有被分发attahInfo,当然其也不会回调onAttachedToWindow,可能还有些其他问题,不过其mAttachInfo未赋值,根据前面的分析,其点击事件也不会响应,不会回调onClick方法
标签:控件,调用,生成,dispatchAttachedToWindow,事件,方法,click From: https://www.cnblogs.com/luoliang13/p/18250707