【问题标题】:How does forceLayout() work in AndroidforceLayout() 在 Android 中是如何工作的
【发布时间】:2017-07-29 00:05:46
【问题描述】:

this answer我写了

forceLayout()

如果您只想重新布局自己的视图,请致电 forceLayout() 内容,但不需要触发对整个视图树的重新测量 (所有父视图)。如果你有一个自定义 ViewGroup 那不是 改变自己的大小,但需要重新测量和重新布局 孩子们,这将是一个合适的情况 forceLayout().

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

我记得我是通过阅读documentationsource code 来写答案的。但是,我没有使用过forceLayout。一位用户 commented 说它没有像我描述的那样工作。

测试 forceLayout

我终于开始研究造成这种情况的原因了。我与祖父母ViewGroup、父母ViewGroup 和孩子View 建立了一个简单的项目。我为它们中的每一个都使用了自定义视图,以便我可以查看 onMeasureonLayoutonDraw 中的日志语句。

当第一次从 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

强制布局

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

孩子

childView.forceLayout();
// (no log output)

父母

viewGroupParent.forceLayout();
// (no log output)

祖父母

viewGroupGrandparent.forceLayout();
// (no log output)

大家一起

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

// MyChildView onDraw called

请求布局

另一方面,调用requestLayout 的效果要大得多。

孩子

childView.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// MyChildView onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called
// MyChildView onLayout called
// MyChildView onDraw called

父母

viewGroupParent.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called

祖父母

viewGroupGrandparent.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupGrandparent onLayout called

问题

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>

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();
    }
}

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);
    }
}

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);
    }
}

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);
    }
}

【问题讨论】:

  • This looks like reasonable output 我对那个输出有点困惑,因为docs 告诉:The measuring pass is implemented in measure(int, int) and is a top-down traversal of the View tree。您能否澄清一下,为什么在这种情况下它是自下而上的遍历?
  • @azizbekian,我猜这是因为我在 onMeasure 方法中调用 super 之后有 Log 语句。
  • 没错,现在logs are printed 的顺序正确。我认为编辑问题是有意义的,以免其他读者感到困惑。
  • @azizbekian,好的,我用重新排序的 Log 语句编辑了问题。

标签: android android-layout view


【解决方案1】:

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

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

        super.requestLayout();
}

在这里,通过调用forceLayout(),TableLayout 的每个子项都将被标记为在未来的布局传递中进行测量。如果在每个孩子上调用requestLayout(),则会发生类似的处理,但requestLayout() 会在视图层次结构中冒泡,因此TableLayout 的孩子的requestLayout() 将调用其父母的requestLayout()。这将设置一个无限循环,其中 TableLayout 及其子级相互调用。 forceLayout() 强制测量而没有无限递归的威胁。


forceLayout() 不会像声明的那样在其父级上调用requestLayout(),但会清除视图的缓存并设置几个标志。

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

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();
    }
    ...
}

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

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

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

Section A - 在本节中,forceLayout() 被称为三个视图。正如Suragch 所指出的,当在所有视图上调用forceLayout() 时,除了调用onDraw() 之外,什么都没有发生。这是 A 部分的日志:

I/MainActivity: 1****************************************** *
I/MainActivity:2************************************************
I/MainActivity: 3************************************************
I/MainActivity: 4************************************************
I/MainActivity:5************************************************
I/MainActivity:6************************************************
I/MainActivity: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************************************************
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: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupParent:onMeasure 调用
我/测量:MyChildView (1)
I/MyChildView:onMeasure 调用 (1)
我/测量: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: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupParent:onMeasure 调用
我/测量:MyChildView (1)
I/MyChildView:onMeasure 调用 (1)
我/测量:MyChildView (2)
I/ViewGroupGrandparent:onLayout 调用
I/ViewGroupParent: onLayout 调用
I/MyChildView: onLayout 调用 (1)
I/MyChildView: onDraw 调用 (1)

C 部分 - 这里是有趣的地方。对于第 10 行和第 11 行,在子视图上调用 requestLayout(),在子视图的父视图上调用 forceLayout()。结果是我们在 B 部分中看到的后续测量/布局/绘制通道不会发生。我相信这就是为什么fluidsonic说forceLayout()坏了。见https://stackoverflow.com/a/44781500/6287910

实际上,子视图考虑在父视图上调用requestLayout(),但发现已经在父视图上请求了一个布局。 (mParent != null &amp;&amp; !mParent.isLayoutRequested())。这是link 的代码isLayoutRequested()

public boolean isLayoutRequested() {
    return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
}

记住forceLayout() 设置了PFLAG_FORCE_LAYOUT 标志。这就是requestLayout() 链在父节点处停止的原因。这可能是一个问题,也可能只是滥用forceLayout()

继续 C 部分的其余部分,我们最多可以“强制”调用孩子的onDraw()

这是 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 调用 (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 调用 (1)
I/MainActivity:14************************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=true (1)
I/MyChildView: onDraw 调用 (1)
I/MainActivity:15************************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested=true (1)
I/MyChildView: onDraw 调用 (1)

D 部分 - 此部分可能包含forceLayout() 的秘密。在第 16 行,对父级上的 requestLayout() 的调用会导致父级和祖父级而不是子级的度量/布局传递。如果对孩子进行了对forceLayout() 的调用,则包含该孩子。事实上,调用的是孩子的onMeasure(),而不是调用其兄弟的onMeasure()。这是由于对孩子的forceLayout() 的调用。因此,似乎在这里forceLayout() 被用来强制框架测量通常不会被测量的孩子。我会注意到,这似乎只发生在 forceLayout() 被调用 requestLayout() 的目标视图的 _direct 后代时。

这种类型的处理的一个例子是在 TableLayout 中。在 TableLayout 的 requestLayout() 覆盖中,forceLayout() 会在每个子节点上调用。这将避免在每个孩子上调用 requestLayout() 以及相关的开销(尽管可能很小)。它还可以避免灾难性的递归,因为孩子的requestLayout() 可能会调用父母的requestLayout(),而父母的requestLayout() 会调用孩子的......你明白了。这里是code from TableLayout

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

        super.requestLayout();
}

在ListView.java中,重用前需要重新测量一个child 见代码hereforceLayout() 在这里为孩子重新测量。

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

这是 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: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupParent:onMeasure 调用
我/测量:MyChildView (1)
我/测量:MyChildView (2)
I/ViewGroupGrandparent:onLayout 调用
I/ViewGroupParent: onLayout 调用
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: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupParent:onMeasure 调用
我/测量:MyChildView (1)
I/MyChildView:onMeasure 调用 (1)
我/测量:MyChildView (2)
I/ViewGroupGrandparent:onLayout 调用
I/ViewGroupParent: onLayout 调用
I/MyChildView: onLayout 调用 (1)
I/MyChildView: onDraw 调用 (1)

E 部分 - 本部分进一步说明,只有调用requestLayout() 的目标视图的直接后代似乎参与触发的布局传递。第 34 和 35 行似乎表明嵌套视图可以链接。

这是 E 部分的日志:

I/MainActivity: 32****************************************** *
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupGrandparent:onLayout 调用
I/MainActivity:33************************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupGrandparent:onLayout 调用
I/MainActivity:34************************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupParent:onMeasure 调用
我/测量:MyChildView (1)
我/测量:MyChildView (2)
I/ViewGroupGrandparent:onLayout 调用
I/ViewGroupParent: onLayout 调用
I/MainActivity:35************************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested=false (null)
I/requestLayout: 线性布局 (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: 线性布局 (null)
I/requestLayout:LinearLayout isLayoutRequested=false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested=false (null)
I/措施:ViewGroupGrandparent(空)
I/ViewGroupGrandparent:onMeasure 调用
I/措施:ViewGroupParent(空)
I/ViewGroupParent:onMeasure 调用
我/测量:MyChildView (1)
I/MyChildView:onMeasure 调用 (1)
我/测量:MyChildView (2)
I/ViewGroupGrandparent:onLayout 调用
I/ViewGroupParent: onLayout 调用
I/MyChildView: onLayout 调用 (1)
I/MyChildView: onDraw 调用 (1)

所以这是我对 forceLayout() 的总结:当有孩子需要重新测量时使用它,例如在 TableLayout 中,并且您不想在每个孩子上调用 requestLayout() - forceLayout()重量更轻,将避免递归。 (请参阅 C 部分中的注释。)forceLayout() 也可用于在需要时强制重新测量特定的直接子项,而这些子项通常不会被测量。 forceLayout() 不能单独工作,必须与对 requestLayout() 的适当调用配对

【讨论】:

  • 分析得很透彻!
  • 让我看看我是否理解正确:forceLayout() 本身实际上并没有启动任何类型的布局传递,但是如果在视图上调用它,那么该视图将包含在父布局中通过(如果父母恰好有一个)。对吗?
  • @Suragch 是的,就是这样。使用 TableLayout,在子级上调用 forceLayout(),并且 TableLayout 启动 requestLayout() 链,该链在层次结构中冒泡。当布局过程作为结果启动时,TableLayout 的子项包括在内。如果没有对forceLayout() 的调用,他们就不会被包括在内。我还没有用 TableLayout 测试过这个,但我知道它是有道理的。我读到的另一张海报将其比作 *nix touch 命令 - 现在不要做任何事情,但在时间和时间将是现在而不是某个模糊的未来时间时包括我。
  • @Suragch 在我的脑海中,我会说,当父级更改(布局参数等)时,它可能会影响其子级。 forceLayout() 导致视图清除其过去测量的缓存。如果视图的缓存被清除,则调用onMeasure(),因为缓存为空;否则,使用缓存和onMeasure() 无关。如果你看一下日志,我想你会看到measure() 被调用,而onMeasure() 不是。我会再看一下,但我认为这是对的。我希望这会有所帮助。这也可能因版本而异。我正在查看 API 25。
  • @Suragch VIews 在父母搭建舞台时衡量自己。我写了一个sample app,它演示了为什么 TableLayout 对其子级调用 forceLayout() 。有关说明,请参见 MainActivity.java 中的 cmets。我希望这有助于澄清一些事情。
猜你喜欢
  • 2011-11-01
  • 1970-01-01
  • 2016-08-11
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 2012-06-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多