深入解析:performTraversals至三大流程的调用路径

更新:11-07 民间故事 我要投稿 纠错 投诉

大家好,今天来为大家解答深入解析: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()。但由于各种判断,测量和布局的数量远远少于所写的。

我能力有限,还是不明白为什么要这样设计,做两次完整的测量和一次不完整的测量。

用户评论

枫无痕

这是哪种编程语言啊?很想知道这种traversal的操作是怎么实现的。

    有15位网友表示赞同!

温柔腔

看来这篇文章要讲如何使用`performTraversals`这个函数吧,希望能详细讲解下它的使用方法。

    有16位网友表示赞同!

你的眸中有星辰

三大流程是什么呢?听起来很高级的样子,想了解更多细节。

    有9位网友表示赞同!

醉红颜

平时学习树结构的时候,这些Traversal操作都是必备的技能,这篇文章很有帮助!

    有19位网友表示赞同!

有阳光还感觉冷

我比较好奇`performTraversals`这个函数调用了哪些具体代码逻辑。

    有14位网友表示赞同!

人心叵测i

讲清楚这三种流程之间的区别和联系吧!

    有12位网友表示赞同!

予之欢颜

文章能不能配上图解,这样更容易理解这些操作的执行过程。

    有10位网友表示赞同!

迷路的男人

如果是在Java语言中使用,希望能解释一下具体的代码示例。

    有16位网友表示赞同!

花花世界总是那么虚伪﹌

三大流程调用流程涉及到哪些关键点?

    有7位网友表示赞同!

强辩

`performTraversals` 到底负责什么工作啊?它与三大流程的关系是什么呢?

    有15位网友表示赞同!

淡抹丶悲伤

这篇文章的学习重点在哪里?我需要先掌握哪些基本知识才能看懂。

    有20位网友表示赞同!

苏莫晨

想通过这篇文章来了解如何更好地运用Traversal操作,提高代码的可读性和效率。

    有17位网友表示赞同!

孤自凉丶

有没有什么实际应用场景可以结合到这些Traversal操作和流程中讲解?

    有17位网友表示赞同!

雨后彩虹

能否详细解释一下每种Traversal操作的特点和适用场景?

    有5位网友表示赞同!

一个人的荒凉

期待作者能用通俗易懂的语言阐释这篇文章的要点。

    有15位网友表示赞同!

掉眼泪

这篇文章应该对学习树数据结构非常有用,值得仔细阅读!

    有15位网友表示赞同!

你是梦遥不可及

希望能在这个基础上更深入地学习Traversal操作的优化和应用技巧。

    有14位网友表示赞同!

窒息

希望作者能够分享一些经验,让我们更好地掌握这些高级算法。

    有7位网友表示赞同!

凉凉凉”凉但是人心

总而言之,这篇文章让我对`performTraversals`和三大流程有了初步了解,期待进一步深化理解。

    有13位网友表示赞同!

【深入解析:performTraversals至三大流程的调用路径】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:探索母爱的伟大与深远——母亲的故事 下一篇:2024年热门网络赚钱机会全面解析