forceLayout()如何在Android中运行

Sur*_*gch 17 android view android-layout

这个答案我写道

forceLayout()

forceLayout()如果您只想重新传输自己视图的内容,则调用,但不需要触发重新测量整个视图树(所有父视图).如果您的自定义ViewGroup没有改变自己的大小但需要重新测量并重新布局其子节点,那么这将是适当的调用情况 forceLayout().

基本上,调用会requestLayout()导致调用 parent.requestLayout(),但调用forceLayout()不会.

我记得我通过阅读文档源代码写了我的答案.但是,我没有使用过forceLayout.一位用户评论说它没有像我描述的那样工作.

测试forceLayout

我终于开始研究这个原因了.我与祖父母ViewGroup,父母ViewGroup和孩子建立了一个简单的项目View.我用自定义视图为他们每个人,这样我可以看日志报表onMeasure,onLayoutonDraw.

首次从xml创建布局时,我得到以下日志:

ViewGroupGrandparent onMeasure called
ViewGroupParent onMeasure called
MyChildView onMeasure called
ViewGroupGrandparent onMeasure called
ViewGroupParent onMeasure called
MyChildView onMeasure called
ViewGroupGrandparent onLayout called
ViewGroupParent onLayout called
MyChildView onLayout called
MyChildView onDraw called
Run Code Online (Sandbox Code Playgroud)

forceLayout

这看似合理的输出.但是,当我随后forceLayout单独调用任何一个视图时,我什么也得不到.如果我一次调用它们,那么子视图onDraw就会被调用.

儿童

childView.forceLayout();
// (no log output)
Run Code Online (Sandbox Code Playgroud)

viewGroupParent.forceLayout();
// (no log output)
Run Code Online (Sandbox Code Playgroud)

祖父母

viewGroupGrandparent.forceLayout();
// (no log output)
Run Code Online (Sandbox Code Playgroud)

全部一起

childView.forceLayout();
viewGroupParent.forceLayout();
viewGroupGrandparent.forceLayout();

// MyChildView onDraw called
Run Code Online (Sandbox Code Playgroud)

requestLayout

另一方面,呼叫requestLayout有更大的影响.

儿童

childView.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// MyChildView onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called
// MyChildView onLayout called
// MyChildView onDraw called
Run Code Online (Sandbox Code Playgroud)

viewGroupParent.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called
Run Code Online (Sandbox Code Playgroud)

祖父母

viewGroupGrandparent.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupGrandparent onLayout called
Run Code Online (Sandbox Code Playgroud)

什么时候forceLayout有效?为什么它似乎不像我上面的例子中那样工作?

补充代码

这是我用来进行上述测试的代码.

activity_main.xml中

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.forcelayout.MainActivity">

    <com.example.forcelayout.ViewGroupGrandparent
        android:id="@+id/view_group_grandparent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <com.example.forcelayout.ViewGroupParent
            android:id="@+id/view_group_parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <com.example.forcelayout.MyChildView
                android:id="@+id/child_view"
                android:layout_width="100dp"
                android:layout_height="100dp"/>
        </com.example.forcelayout.ViewGroupParent>
    </com.example.forcelayout.ViewGroupGrandparent>

    <Button
        android:text="Click me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="buttonClick"/>

</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void buttonClick(View view) {
        Log.i("TAG", "buttonClick: ");

        ViewGroupGrandparent viewGroupGrandparent = (ViewGroupGrandparent) findViewById(R.id.view_group_grandparent);
        ViewGroupParent viewGroupParent = (ViewGroupParent) findViewById(R.id.view_group_parent);
        MyChildView childView = (MyChildView) findViewById(R.id.child_view);


        childView.forceLayout();
        //viewGroupParent.forceLayout();
        //viewGroupGrandparent.forceLayout();

        //childView.requestLayout();
        //viewGroupParent.requestLayout();
        //viewGroupGrandparent.requestLayout();
    }
}
Run Code Online (Sandbox Code Playgroud)

ViewGroupGrandparent.java

public class ViewGroupGrandparent extends LinearLayout {

    public ViewGroupGrandparent(Context context) {
        super(context);
    }

    public ViewGroupGrandparent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ViewGroupGrandparent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("TAG", "ViewGroupGrandparent onMeasure called");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i("TAG", "ViewGroupGrandparent onLayout called");
        super.onLayout(changed, l, t, r, b);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i("TAG", "ViewGroupGrandparent onDraw called");
        super.onDraw(canvas);
    }
}
Run Code Online (Sandbox Code Playgroud)

ViewGroupParent.java

public class ViewGroupParent extends LinearLayout {

    public ViewGroupParent(Context context) {
        super(context);
    }

    public ViewGroupParent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ViewGroupParent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("TAG", "ViewGroupParent onMeasure called");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i("TAG", "ViewGroupParent onLayout called");
        super.onLayout(changed, l, t, r, b);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i("TAG", "ViewGroupParent onDraw called");
        super.onDraw(canvas);
    }
}
Run Code Online (Sandbox Code Playgroud)

MyChildView.java

public class MyChildView extends View {

    public MyChildView(Context context) {
        super(context);
    }

    public MyChildView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyChildView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("TAG", "MyChildView onMeasure called");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        Log.i("TAG", "MyChildView onLayout called");
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i("TAG", "MyChildView onDraw called");
        super.onDraw(canvas);
    }
}
Run Code Online (Sandbox Code Playgroud)

Che*_*amp 19

TL; DR考虑TableLayout中的以下代码:

public void requestLayout() {
        if (mInitialized) {
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                getChildAt(i).forceLayout();
            }
        }

        super.requestLayout();
}
Run Code Online (Sandbox Code Playgroud)

这里TableLayout的每个子项都将被标记为在将来的布局传递中通过调用来测量forceLayout().如果requestLayout()在每个子节点上调用,则会发生类似的处理,但是requestLayout()在视图层次上会冒泡,因此requestLayout()TableLayout的子节点将调用其父节点requestLayout().这将使用TableLayout建立一个无限循环,并且它的子节点彼此调用.forceLayout()在没有无限递归威胁的情况下强制测量.


forceLayout()没有requestLayout()按照规定调用其父级,但清除视图的缓存并设置几个标志.

public void forceLayout() {
    if (mMeasureCache != null) mMeasureCache.clear();
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;
}
Run Code Online (Sandbox Code Playgroud)

requestLayout()清除缓存并设置这些相同的标志,forceLayout()但也可以调用requestLayout()父级.

public void requestLayout() {
    if (mMeasureCache != null) mMeasureCache.clear();
    ...
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;

    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

requestLayout() 应该在整个层次结构中冒泡.

那么,forceLayout()实际上做了什么?为了研究这个问题,我把所提供的应用程序,并修改其跟踪到来电onMeasure(),onLayout()onDraw()为两个视图组(祖父母和父母)和子视图.我给第一个孩子添加了一个兄弟姐妹,比较他们两个孩子的情况.我还使用调试器跟踪调用measure()requestLayout().输出是在logcat中捕获的,并且从logcat生成了一个总结操作的电子表格.(此答案中的源代码和文档参考已在此GitHub项目中编目.

测试应用程序的调用forceLayout()requestLayout()两个视图组和子视图中的所有可能的组合- 64在所有.(其中许多组合在现实世界中并不现实,但为了完整性而包括在内.)下面的电子表格总结了讨论的关键领域.完整的工作表可以在GitHub存储库中找到.

在此输入图像描述

A部分 - 在本节中,forceLayout()对三个视图进行调用.正如Suragch指出的那样,除了在所有视图上onDraw()调用时forceLayout()调用之外没有任何其他事情发生.这是A部分的日志:

I/MainActivity:1*******************************************
I /主要活动:2*******************************************
I/MainActivity: 3*********************************************
I/MainActivity:4*******************************************
我/主要活动:5*******************************************
我/主要活动:6*******************************************
我/主要活动:7*******************************************
I/MyChildView:onDraw调用(1)

"1****..."对应于电子表格中的行.诸如"I/MyChildView:onDraw called(1)"之类的行标识视图("MyChildView"),视图方法("onDraw")和"(x)"将为第一个子视图"(1)"," (2)"对于第二个子视图,"(null)"用于其他非子视图.

考虑到方法的名称,这是一个意外的结果:forceLayout().

B部分 - 在第8行,requestLayout()在子视图上调用,结果是预期的:在所有视图上都采用度量,布局和绘图传递.第9行添加了forceLayout()对孩子的呼叫,但结果是相同的.这是B部分的日志:

/ MainActivity:8*******************************************
我/ requestLayout :MyChildView(1)
I/requestLayout:MyChildView isLayoutRequested = false(1)
I/requestLayout:ViewGroupParent(null)
I/requestLayout:ViewGroupParent isLayoutRequested = false(null)
I/requestLayout:ViewGroupGrandparent(null)
I/requestLayout:ViewGroupGrandparent isLayoutRequested = false(null)
I/requestLayout:LinearLayout(null)
I/requestLayout:LinearLayout isLayoutRequested = false(null)
I/requestLayout:ContentFrameLayout(null)
I/requestLayout:ContentFrameLayout isLayoutRequested = false(null)
I/requestLayout:ActionBarOverlayLayout(null)
I/requestLayout:ActionBarOverlayLayout isLayoutRequested = false(null)
I/requestLayout:FrameLayout(null)
I/requestLayout:FrameLayout isLayoutRequested = false(null)
I/requestLayout:LinearLayout(null)
I/requestLayout:LinearLayout isLayoutRequested = false(null)
I/requestLayout:DecorView(null)
I/requestLayout:DecorView isLayoutRequested = false(null)
I/measure:ViewGroupGrandparent(null)
I/ViewGroupGrandparent:onMeasure名为
I/measure:ViewGroupParent(null)
I/ViewGroupParent:onMeasure名为
I/measure: MyChildView(1)
I/MyChildView:onMeasure叫(1)
I/measure:MyChildView(2)
I/ViewGroupGrandparent:onLayout调用
I/ViewGroupParent:onLayout调用
I/MyChildView:onLayout调用(1)
I/MyChildView:onDraw调用(1)
I/MainActivity:9*******************************************
I/requestLayout:MyChildView(1)
I/requestLayout:MyChildView isLayoutRequested = false(1)
I/requestLayout :ViewGroupParent(null)
I/requestLayout:ViewGroupParent isLayoutRequested = false(null)
I/requestLayout:ViewGroupGrandparent(null)
I/requestLayout:ViewGroupGrandparent isLayoutRequested = false(null)
I/requestLayout:LinearLayout(null)
I/requestLayout:LinearLayout isLayoutRequested = false(null)
I/requestLayout:ContentFrameLayout(null)
I/requestLayout:ContentFrameLayout isLayoutRequested = false(null)
I/requestLayout:ActionBarOverlayLayout(null)
I/requestLayout:ActionBarOverlayLayout isLayoutRequested = false(null)
I/requestLayout:FrameLayout(null)
I/requestLayout: FrameLayout isLayoutRequested = false(null)
I/requestLayout:LinearLayout(null)
I/requestLayout:LinearLayout isLayoutRequested = false(null)
I/requestLayout:DecorView(null)
I/requestLayout:DecorView isLayoutRequested = false(null)
I/measure:ViewGroupGrandparent (null)
I/ViewGroupGrandparent:onMeasure名为
I/measure:ViewGroupParent(null)
I/ViewGroupParent:onMeasure名为
I/measure:MyChildView(1)
I/MyChildView:onMeasure名为(1)
I/measure:MyChildView(2)
I/ViewGroupGrandparent:onLayout名为
I/ViewGroupParent:onLayout名为
I/MyChildView:onLayout调用( 1)
I/MyChildView:onDraw调用(1)

C部分 - 这是事情变得有趣的地方.对于第10行和第11行,requestLayout()在子视图上调用,并在子视图forceLayout()的父视图上调用.结果是我们在B节中看到的后续测量/布局/绘制过程没有发生.我相信这就是为什么流体说它forceLayout()被打破了.请参阅/sf/answers/3134705031/.

In fact, the child view considers calling requestLayout() on the parent but finds that a layout has already been requested on the parent. (mParent != null && !mParent.isLayoutRequested()). Here is a link to the code for isLayoutRequested().

public boolean isLayoutRequested() {
    return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
}
Run Code Online (Sandbox Code Playgroud)

Remember that forceLayout() set the PFLAG_FORCE_LAYOUT flag. This is why the requestLayout() chain stops at the parent. This could be an issue or just a misuse of forceLayout().

Continuing on with the rest of section C, the most we can "force" is a call to the child's onDraw().

Here is the log for section C:

I/MainActivity: 10*******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=true (1)
I/MainActivity: 11*******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=true (1)
I/MainActivity: 12*******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=false (1)
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested=true (null)
I/MyChildView: onDraw called (1)
I/MainActivity: 13*******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=false (1)
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested=true (null)
I/MyChildView: onDraw called (1)
I/MainActivity: 14*******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=true (1)
I/MyChildView: onDraw called (1)
I/MainActivity: 15*******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=true (1)
I/MyChildView: onDraw called (1)

Section D - This section may hold the secret to forceLayout(). On line 16, a call to requestLayout() on the parent results in a measure/layout pass for the parent and the grandparent but not the child. If a call to forceLayout() is made on the child, then the child is included. In fact, a call is made to the child's onMeasure() while a call is not made to its sibling's onMeasure(). This is due to the call to forceLayout() on the child. So, it seems, that here forceLayout() is being used to force the framework to measure a child that would not ordinarily be measured. I will note that this only seems to happen when forceLayout() is called on a _direct descendent of the target view of requestLayout().

One such example of this type of processing is in TableLayout. In the requestLayout() override in TableLayout, forceLayout() is called on each child. This will avoid calling requestLayout() on each child and the associated overhead that would entail (although probably small). It will also avoid disastrous recursion since the child's requestLayout() may call the parent's requestLayout() that will call the child's...you get the idea. Here is the code from TableLayout:

public void requestLayout() {
        if (mInitialized) {
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                getChildAt(i).forceLayout();
            }
        }

        super.requestLayout();
}
Run Code Online (Sandbox Code Playgroud)

In ListView.java, there is a need to remeasure a child before reuse See the code here. forceLayout() works here to get the child remeasured.

// Since this view was measured directly aginst the parent measure
// spec, we must measure it again before reuse.
child.forceLayout();
Run Code Online (Sandbox Code Playgroud)

Here is the log for section D:

I/MainActivity: 16*******************************************
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested=false (null)
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested=false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested=false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure called
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure called
I/measure: MyChildView (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout called
I/ViewGroupParent: onLayout called
I/MainActivity: 17*******************************************
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested=false (null)
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested=false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested=false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure called
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure called
I/measure: MyChildView (1)
I/MyChildView: onMeasure called (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout called
I/ViewGroupParent: onLayout called
I/MyChildView: onLayout called (1)
I/MyChildView: onDraw called (1)

Section E - This section further demonstrates that only direct descendents of the target view of a call to requestLayout() seems to participate in the triggered layout passes. Lines 34 and 35 seem to indicate that a nested views can chain.

Here is the log for section E:

I/MainActivity: 32*******************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested=false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested=false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure called
I/measure: ViewGroupParent (null)
I/ViewGroupGrandparent: onLayout called
I/MainActivity: 33*******************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested=false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested=false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure called
I/measure: ViewGroupParent (null)
I/ViewGroupGrandparent: onLayout called
I/MainActivity: 34*******************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested=false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested=false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure called
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure called
I/measure: MyChildView (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout called
I/ViewGroupParent: onLayout called
I/MainActivity: 35*******************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested=false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested=false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested=false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure called
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure called
I/measure: MyChildView (1)
I/MyChildView: onMeasure called (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout called
I/ViewGroupParent: onLayout called
I/MyChildView: onLayout called (1)
I/MyChildView: onDraw called (1)

所以这是我的外卖forceLayout():当有孩子需要重新测量时使用它,例如TableLayout,你不想打电话requestLayout()给每个孩子 - forceLayout()重量更轻,避免递归.(参见C部分的注释)forceLayout()也可用于强制在需要时重新测量特定的直接儿童,而这些儿童通常不会被测量.forceLayout()不能单独工作,必须与适当的电话配对requestLayout()