5.requestLayout
问题
其一:锁屏后,调用 View.requestLayout (),会往上层层调用 requestLayout () 吗?
其二:锁屏后,调用 View.requestLayout (),会触发 View 的测量和布局操作吗?
第一层(往上,层层遍历)
假设调用I.requestLayout (),会触发哪些 View 的 requestLayout 方法?
答:会依次触发I.requestLayout () -> C.requestLayout () -> A.requestLayout () -> … 省略一些 View -> ViewRootImpl.requestLayout ()
1 | |
该方法作用如下:
- 清除测量记录
- 增加PFLAG_FORCE_LAYOUT给mPrivateFlags
- 如果mParent没有调用过requestLayout,则调用之。换句话说,如果调用过,则不会继续调用
重点看下mParent.isLayoutRequested()方法,它在View.java中有具体实现
1 | |
在View调用完layout方法,会将PFLAG_FORCE_LAYOUT标志位清除掉。当View下次再调用requestLayout方法时,依旧能往上层层调用。但是如果当layout()方法没有执行时,下次再调用requestLayout方法时,就不会往上层层调用了。
其一:锁屏后,调用 View.requestLayout (),会往上层层调用 requestLayout () 吗?
答:锁屏后,除了第一次调用会往上层层调用,其它的都不会
为什么,只有第一次调用会呢?那必定是因为 layout 方法没有得到执行,导致 PFLAG_FORCE_LAYOUT 无法被清除
第二层(ViewRootImpl.requestLayout)
1 | |
该方法主要作用如下:
- 往主线程的Handler对应的MessageQueue发送一个同步屏障消息
- 将mTraversalRunnable保存到Choreographer中
此处有三个特别重要的知识点:
- mTraversalRunnable
- MessageQueue的同步屏障
- Choreographer机制 [[2.Handler 里的各种消息#Choreographer]]
mTraversalRunnable相对比较简单,它的作用就是从ViewRootImpl 从上往下执行performMeasure、performLayout、performDraw。
它的执行时机是当Vsync信号来到时,会往主线程的Handler对应的MessageQueue中发送一条异步消息,由于在scheduleTraversals()中给MessageQueue中发送过一条同步屏障消息,那么当执行到同步屏障消息时,会将异步消息取出执行
1 | |
该方法的作用:
- 满足条件的情况下调用performMeasure()
- 满足条件的情况下调用performLayout()
- 满足条件的情况下调用performDraw()
mStopped表示Activity是否处于stopped状态。如果Activity调用了onStop方法,performLayout方法是不会调用的。
其二:锁屏后,调用 View.requestLayout (),会触发 View 的测量和布局操作吗?
答:不会,因为当前 Activity 处于 stopped 状态了,因为不会执行View.layout()方法,所以PFLAG_FORCE_LAYOUT不会被清除,导致接下来的requestLayout方法不会层层往上调用。
其三:既然 Activity 的 onStop 会导致 requestLayout layout 方法得不到执行,那么 onResume 方法会不会让上一次的 requestLayout 没有执行的 layout 方法执行一次呢?
亮屏调用了performTraversals方法时,会执行Measure、Layout、Draw等操作
![[Pasted image 20250820154352.png]]
requestLayout 后没有 onMearsu
requestLayout 后没有 onMearsure
getItem 的实际堆栈
![[Pasted image 20250714102910.png]]
onMeasure () → populate () → addNewItem () → adapter.instantiateItem () → getItem ()
其实 viewpager setAdapter 里会调用 requestlayout 但是没有执行 onMeasure,原因是因为 view 测量的时候若 View 的可见性为 View. GONE,系统会跳过其测量流程。而 viewpager 的父 view 抽屉 view 第一次设置的就是 gone,所以没有调用 onMeasure
![[Pasted image 20250714102944.png]]
修改:第一次切换抽屉模式后设置抽屉 view 为 INVISIBLE
requestLayout 后没传到父 view
将所有应用分为3个文件夹放置 dock 栏,切换布局规则,文件夹大小无变化,长按后更新大小
按理来说:requestLayout 是向上传递,调用子 View 的 requestLayout () 会递归向上传递到父 View,这是因为 dock 子 view requestLayout 了,但是没有传到 dock,然后 onMeasure 没有执行。
通过分析源码 requestLayout 方法:mParent.isLayoutRequested () 是 false 才能执行父 view requestLayout。
通过查找是 dock 在桌面加载的时候调用了 forceLayout,这个方法会导致 isLayoutRequested 成 true,但是没有 measure,所以这个标志位一直是 true,然后子 view requestLayout 就不会刷新父 view
最后修改,取消了 forcelayout ,直接修改 view 的属性 setBlur
requestLayout()需要逐层向上触发父布局的requestLayout(),最终到达ViewRootImpl。
FolderIcon1x1 isLayoutRequested 是 true,如下面源码所示,所以父 view requestLayout 没有执行,而 hotset 的 isLayoutRequested 是 false
![[Pasted image 20250619155726.png]]
为什么 FolderIcon1x1 isLayoutRequested 是 true?
PFLAG_FORCE_LAYOUT 就是防止重复调用的,如果调用过,则不会继续调用,测量布局后会清掉
isLayoutRequested 源码
请求布局阶段:
- 调用
requestLayout()或forceLayout() - 操作:设置
PFLAG_FORCE_LAYOUT标志,强制下一次 measure 一定要重新执行。 - 结果:
isLayoutRequested()返回 true,系统知道该 View 需要重新布局
布局处理阶段:
- 进入
measure()或layout()方法 - 操作:这个标志位会在 真正执行
measure()过程中清掉。 - 结果:完成测量和布局之后,
isLayoutRequested()返回 false
![[Pasted image 20250619155934.png]]
[BugFix][Launcher]开启深色模式,文件夹拖拽到 dock 栏,文件夹显示黑色,开启浅色模式,dock 栏的文件夹黑色没有变化
[RootCause]: 从设置更改深色浅色模式的时候 dock 栏的子 view 不会 onlayout
[Modify]: 在 hotseats 执行 onlayout 的时候对子 view 遍历执行 layout
最后修改,取消了 forcelayout ,直接修改 setBlur
https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiHome/+/5278057
dock 在桌面加载的时候 forceLayout,导致 isLayoutRequested 成 true,但是没有 measure,所以这个标志位一直是 true,然后子 view requestLayout 就不会刷新父 view
子 view 是否需要调用 requestLayout
mPreviewContainer.requestLayout ()
mImageView.requestLayout ()
mFolderCover.requestLayout ()
如果自己 requestLayout 后,子 view 是否需要调用 requestLayout?
需要,因为需要子 view requestLayout,会设置标记位,有这个标记位,父 view 的 onMeasure 才会调用子 view 的 onMeasure
![[Pasted image 20250619155456.png]]
- 3个子 view requestLayout 都完了,父 view requestLayout 才会执行,只调一次,系统合并优化
- 子 view 大小改变,然后导致父 view requestLayout 才会执行
- 父 View 的 MeasureSpec 未变化,导致子 View 的 onMeasure () 未被调用。所以调用子 view requestLayout,子 view onMeasure 没执行
- requestLayout 是向上传递。不会向下传递,调用子 View 的 requestLayout () 会递归向上传递到父 View,直到根 View
https://wayawbott0.f.mioffice.cn/docx/doxk4JoJmzat1c2edpyF5CvdFcJ