大家好,今天来为大家解答深入解析:performTraversals至三大流程的调用路径这个问题的一些问题点,包括也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~
几天前,我在嵌套在RelativeLayout中的自定义View的onMeasure()、onLayout()和onDraw()中打印信息。它实际上输出了四个小节和两个布局。部分原因是RelativeLayout 测量了子级两次。看法。另外,我只知道performTraversals()是三大流程的起点,但不知道怎么调用,所以……
以下是您在开始之前应该查看的一些基础知识。
PerformTraversals()简单分析Measure流程、Layout流程、Draw流程
一、调用过程的结论
API26从performTraversals()到三大流程的运行步骤如下。
第一次performTraversals()
调用第一个measureHierarchy()。调用performMeasure()。调用视图的measure()。 DecorView 的onMeasure() 被递归调用。 (onMeasure 第一次)第一次调用PerformMeasure()。调用视图的measure()。 DecorView 的onMeasure() 被递归调用。 (第二次onMeasure)调用performLayout()。 layout() 被调用onLayout() 被调用,递归到最后。 (第一次onLayout)performDraw()没有被调用,新一轮的performTraversals()开始了。
第二次performTraversals()
调用第一个measureHierarchy()。调用performMeasure()。调用视图的measure()。内部自定义View的onMeasure()没有被调用。调用performLayout()。调用布局()。内部自定义View的onLayout()没有被调用。调用performDraw()。调用draw(),调用onDraw(),递归结束。 (第一次onDraw)
二、DecorView结构
测试的布局
Activtiy加载如下布局文件,其中Circle是继承View的自定义View。
?xml version="1.0"encoding="utf-8" ?使用的样式是默认项目的AppTheme。
DecorView结构
如果使用上面的测试布局,DecorView在第一次测量时将具有以下结构:
装饰视图(3)
LinearLayout(2)ViewStub(GONE)FrameLayout(1)ActionBarOverlayLayout(2)ActionBarContainer(2)ToolBar(1)AppCompatTextViewActionBarContextView(GONE)ContentFrameLayout(1,这里是Activity加载的布局) LinearLayout(1)自定义的CircleViewView(id为navigationBarBackgroud) ) View (id为statusBarBackgroud)
注意
ContentFrameLayout中的布局是Activity中setContentView加载的布局。这个DecorView并不是不变的,在后续的测量和布局过程中可能会增加布局。
三、详细流程--第一次performTraversals()
1)第一个measureHierarchy()被调用
这是第一次测量,也是最复杂的一次。所以分析的更详细一点,后续的流程与此类似。
1. measureHierarchy()被调用
调用条件为layoutRequested为true。
boolean layoutRequested=mLayoutRequested(!mStopped || mReportNextDraw);
如果(布局请求){
//.
windowSizeMayChange |=measureHierarchy(主机, lp, res,
期望窗口宽度、期望窗口高度);
}mLayoutRequested 在第1629 行被指定为true,因为mFirst 为true。
如果(mFirst){
//.
mLayoutRequested=true;
//.
}mStopped 被初始化为false 并且始终为false。没有找到任何地方可以为其指定true 值。
mReportNextDraw暂时不要参与,所以默认为false。
最后layoutRequested为true并调用measureHierarchy()。
2. ViewRootImpl#measureHierarchy() -DecorView#measure()
在measureHierarchy()中,由于窗口宽度不是wrap_content,因此未调用1471和1484的performMeasure(),并且goodMasure未赋值为true。 1498的performMeasure()的调用条件是goodMeasure为假,所以调用了这个performMeasure()。
if (lp.width==ViewGroup.LayoutParams.WRAP_CONTENT) {
//.
if (baseSize !=0desiredWindowWidth baseSize) {
//.
执行测量(childWidthMeasureSpec,childHeightMeasureSpec);
//.
if ((host.getMeasuredWidthAndState()View.MEASURED_STATE_TOO_SMALL)==0) {
好测量=真;
} 别的{
//.
执行测量(childWidthMeasureSpec,childHeightMeasureSpec);
if ((host.getMeasuredWidthAndState()View.MEASURED_STATE_TOO_SMALL)==0) {
好测量=真;
}
}
}
}
如果(!好测量){
//.
执行测量(childWidthMeasureSpec,childHeightMeasureSpec);
//.
}只要performMeasure中mView(此时为DecorView)不为空,mView的measure()就会被调用。
私有无效PerformMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
如果(mView==null){
返回;
}
尝试{
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
//.
3. DecorView的第一次测量过程
由于只有View实现了measure(),这是一个final方法,所以所有调用measure()的视图都会直接转到View的measure()。调用mView(DecorView)的measure(),实际上调用的是View统一实现的measure()。
View#measure() -DecorView#onMeasure()
measure()中调用onMeasure的条件是:
最终布尔forceLayout=(mPrivateFlags PFLAG_FORCE_LAYOUT)==PFLAG_FORCE_LAYOUT;
最终布尔型needLayout=specChanged
(sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
if (forceLayout || needLayout) {
//.
int cacheIndex=强制布局? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex 0 || sIgnoreMeasureCache) {
//.
onMeasure(widthMeasureSpec, heightMeasureSpec);
//.sIgnoreMeasureCache 值在API20 之前始终为true,自API20 以来一直为false。这里forceLayout为true,所以onMeasure()被调用。
DecorView的测量分发
到达DecorView的onMeasure(),直接调用其父类(FrameLayout)的onMeasure()。
@覆盖
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//.
到达FrameLayout的onMeasure(),首先统计DecorView的子View数量,三个,一个LinearLayout和两个普通View,分别对它们调用measureChildWithMargins(),measureChildWithMargins()会调用子View的measure() 。
@覆盖
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count=getChildCount();
//.
for (int i=0; i 计数; i++) {
最终视图子=getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() !=GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
//.
}
}
//.
a. DecorView第一个子View--LinearLayout的测量
measureChildWithMargins()调用LinearLayout的measure()(实际上是View的measure())。
protected voidmeasureChildWithMargins(查看子项,
intparentWidthMeasureSpec,int widthUsed,
intparentHeightMeasureSpec, int heightUsed) {
//.
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}这个measure()的forceLayout为true,所以调用了LinearLayout的onMeasure()。进入LinearLayout的onMeasure()后,方向确定,进入measureVertical()。 LinearLayout有两个子View。
i. LinearLayout的第一个子View--ViewStub
第一个是ViewStub,但是这个ViewStub的状态是GONE,所以没有做测量工作。
ii. LinearLayout的第二个子View--FrameLayout
第二个子View是一个FrameLayout,直到FrameLayout的onMeasure()才会被调用。在onMeasure中,断定FrameLayout的子View只有一个,ActionBarOverlayLayout。
ActionBarOverlayLayout的onMeasure()被调用,测量过程被传递给两个子View,ActionBarContainer和ContentFrameLayout。
ActionBarContainer
ActionBarOverlayLayout 在mActionBarTop(即ActionBarContainer)上调用measureChildWithMargins()。 ActionBarContainer的onMeasure()被调用,ActionBarContainer继承了FrameLayout(),在它的onMeasure()中,调用了父类(FrameLayout)的onMeasre()。
ActionBarContainer也有两个子View,一个是Toolbar,调用Toolbar的onMeasure(),而Toolbar中还有另一个子View,AppCompatTextView。这个TextView的绘制过程就不讨论了。
然后我们进入ActionBarContainer的第二个子View,即ActionBarContextView,此时处于GONE状态。 ActionBarContainer的两个子View都已经遍历完了。
ContentFrameLayout
然后ActionBarOverlayLayout对mContent调用measureChildWithMargins(),mContent是ContentFrameLayout,调用ContentFrameLayout的onMeasure(),调用其父类(FrameLayout)的onMeasure()。 ContentFrameLayout有一个子View,它是LinearLayout。
这个LinearLayout就是我们前面代码中定义的布局文件中的LinearLayout。进入measureVertical(),里面有一个子View,就是我自定义的CircleView。 CircleView 的onMeasure() 被调用。 ContentFrameLayout 测量结束。
b. DecorView第二、三个子View的测量
下面两个子View是普通View,一个ID为navigationBarBackgroud,另一个ID为statusBarBackgroud。至此,DecorView的测量完成,返回到ViewRootImp()的performTraversals()。
2)第二个measureHierarchy()没有被调用
mApplyInsetsRequested 为false。
如果(mApplyInsetsRequested){
mApplyInsetsRequested=false;
//.
如果(mLayoutRequested){
//.
windowSizeMayChange |=measureHierarchy(主机, lp,
mView.getContext().getResources(),
期望窗口宽度、期望窗口高度);
}
3)第一个performMeasure()被调用
mStopped 为false,updatedConfiguration 为true。
if (!mStopped || mReportNextDraw) {
布尔focusChangedDueToTouchMode=EnsureTouchModeLocally(
(relayoutResultWindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE)!=0);
if (focusChangedDueToTouchMode || mWidth !=host.getMeasuredWidth()
|| mHeight !=host.getMeasuredHeight() || contentInsetsChanged ||
更新配置){
//.
执行测量(childWidthMeasureSpec,childHeightMeasureSpec); DecorView的第二次测量过程与第一次相同。
4)第二个performMeasure()未被调用
权重全为0,measureAgain为假。
布尔再次测量=false;
if (lp.horizontalWeight 0.0f) {
//.
再次测量=真;
}
if (lp.verticalWeight 0.0f) {
//.
再次测量=真;
}
如果(再次测量){
//.
执行测量(childWidthMeasureSpec,childHeightMeasureSpec);
}
5)performLayout()会被调用
调用条件
layoutRequested 为true,mStopped 为false,mReportNextDraw 为false。 didLayout为true,因此调用performLayout。
Final boolean didLayout=layoutRequested(!mStopped || mReportNextDraw);
//.
如果(didLayout){
执行布局(lp,mWidth,mHeight);
//.
ViewRootImpl#performLayout()-DecorView#onLayout()
只要performLayout()中mView(DecorView)不为空,mView的layout()就会被调用。 DecorView不会重写layout(),因此ViewGroup的layout()会被调用。 ViewGroup调用父类(View)的layout()。 View的布局调用onLayout的条件如下,changed为true,调用DecorView的onLayout()。
布尔值更改=isLayoutModeOptical(mParent) ?
: setOpticalFrame(l,t,r,b);setFrame(l,t,r,b);
if (change || (mPrivateFlags PFLAG_LAYOUT_REQUIRED)==PFLAG_LAYOUT_REQUIRED) {
onLayout(改变, l, t, r, b);
DecorView布局流程分发
DecorView的onLayout会直接调用父类(FrameLayout)的onLayout()。这里遍历了DecorView的子View,并调用每个子View的layout(),与measure过程类似进行分布式。
LinearLayout中没有重写layout(),因此会调用ViewGroup的layout()-View的layout()-LinearLayout的onLayout()-LinearLayout的onLayout()-LinearLayout的layoutVertical()。有两个子View,ViewStub还是不行。存在,那么会在第二个子View FrameLayout上调用setChildFrame(),内部会调用FrameLayout的layout()。然后调用FrameLayout的onLayout(),继续分发到最后。
与measure稍有不同的是,会先处理ContentFrameLayout,然后再处理ActionBarContainer。
6)performDraw()未被调用
newSurface 是真实的。 isViewVisible 为true 并且不会调用PerformDraw()。这里调用了scheduleTraversals(),所以还会有第二个performTraversals()。
如果(!cancelDraw!newSurface){
//.
执行绘图();
} 别的{
如果(isViewVisible){
//再试一次
调度遍历();
}
//.
}
四、详细流程--第二次performTraversals()
1) measureHierarchy()被调用
这里mLayoutRequested为true,mStopped为false,mReportNextDraw为true,因为在上一次performTraversals()中报告了一次。
boolean layoutRequested=mLayoutRequested(!mStopped || mReportNextDraw);
如果(布局请求){
//.
windowSizeMayChange |=measureHierarchy(主机, lp, res,
期望窗口宽度、期望窗口高度);
之后,同样的goodMeasure 为false,并调用PerformMeasure()。大致是一样的,但是为什么这个测量没有进入自定义View的onMeasure呢?这是因为在调用ContentFrameLayout的measure()时,forceLayout为false,所以没有调用ContentFrameLayout的onMeasure()。
与之前的另一个区别是Toolbar 中的onMeasure() 会调用ActionMenuView 的onMeasure()。前两次测量中没有出现ActionMenuView。
2) 第二个measureHierarchy()没被调用
与第一个performTraversals 的原因相同。
3) 第一、二个performMeasure()没被调用
mStopped为假,mReportNextDraw为真,但第二个if不进入,updatedConfiguration为假。
if (!mStopped || mReportNextDraw) {
if (focusChangedDueToTouchMode || mWidth !=host.getMeasuredWidth()
|| mHeight !=host.getMeasuredHeight() || contentInsetsChanged ||
更新配置){
//.
执行测量(childWidthMeasureSpec,childHeightMeasureSpec);
//.
如果(再次测量){
执行测量(childWidthMeasureSpec,childHeightMeasureSpec);
}
布局请求=true;
}
4) performLayout()被调用
DecorView的其他部分仍然调用onLayout()。除了ContentFrameLayout调用layout()的时候,在View的layout()中,changed是false,并且没有设置PFLAG_LAYOUT_REQUIRED,所以他的onLayout()没有被调用。
公共无效布局(int l,int t,int r,int b){
//.
布尔值更改=isLayoutModeOptical(mParent) ?
: setOpticalFrame(l,t,r,b);setFrame(l,t,r,b);
if (change || (mPrivateFlags PFLAG_LAYOUT_REQUIRED)==PFLAG_LAYOUT_REQUIRED) {
onLayout(改变, l, t, r, b);
//.
}
//.
}
5) performDraw()被调用
完成完整的绘制过程。
五、总结
经过这样的分析,三大流程确实清晰了很多。最重要的是三大流程不只是几个单独的方法,调用流程都明白。
没想到简单的布局也能搞得这么复杂。主要原因是现成的主题结构复杂。如果我早知道的话,我会改成没有Toolbar 的主题进行分析.
从表面上看,确实调用了两次performTraversals(),而且方法内部也多次调用了measure()。但由于各种判断,测量和布局的数量远远少于所写的。
我能力有限,还是不明白为什么要这样设计,做两次完整的测量和一次不完整的测量。
【深入解析:performTraversals至三大流程的调用路径】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
这是哪种编程语言啊?很想知道这种traversal的操作是怎么实现的。
有15位网友表示赞同!
看来这篇文章要讲如何使用`performTraversals`这个函数吧,希望能详细讲解下它的使用方法。
有16位网友表示赞同!
三大流程是什么呢?听起来很高级的样子,想了解更多细节。
有9位网友表示赞同!
平时学习树结构的时候,这些Traversal操作都是必备的技能,这篇文章很有帮助!
有19位网友表示赞同!
我比较好奇`performTraversals`这个函数调用了哪些具体代码逻辑。
有14位网友表示赞同!
讲清楚这三种流程之间的区别和联系吧!
有12位网友表示赞同!
文章能不能配上图解,这样更容易理解这些操作的执行过程。
有10位网友表示赞同!
如果是在Java语言中使用,希望能解释一下具体的代码示例。
有16位网友表示赞同!
三大流程调用流程涉及到哪些关键点?
有7位网友表示赞同!
`performTraversals` 到底负责什么工作啊?它与三大流程的关系是什么呢?
有15位网友表示赞同!
这篇文章的学习重点在哪里?我需要先掌握哪些基本知识才能看懂。
有20位网友表示赞同!
想通过这篇文章来了解如何更好地运用Traversal操作,提高代码的可读性和效率。
有17位网友表示赞同!
有没有什么实际应用场景可以结合到这些Traversal操作和流程中讲解?
有17位网友表示赞同!
能否详细解释一下每种Traversal操作的特点和适用场景?
有5位网友表示赞同!
期待作者能用通俗易懂的语言阐释这篇文章的要点。
有15位网友表示赞同!
这篇文章应该对学习树数据结构非常有用,值得仔细阅读!
有15位网友表示赞同!
希望能在这个基础上更深入地学习Traversal操作的优化和应用技巧。
有14位网友表示赞同!
希望作者能够分享一些经验,让我们更好地掌握这些高级算法。
有7位网友表示赞同!
总而言之,这篇文章让我对`performTraversals`和三大流程有了初步了解,期待进一步深化理解。
有13位网友表示赞同!