forceLayout()、requestLayout() 和 invalidate() 的使用

Posted

技术标签:

【中文标题】forceLayout()、requestLayout() 和 invalidate() 的使用【英文标题】:Usage of forceLayout(), requestLayout() and invalidate() 【发布时间】:2012-12-01 03:02:06 【问题描述】:

我对 View 类的 forceLayout()requestLayout()invalidate() 方法的角色有点困惑。

什么时候叫他们?

【问题讨论】:

【参考方案1】:

为了更好地理解François BOURLIEUX 和Dalvik 提供的答案,我建议你看看Arpit Mathur 提供的这个很棒的视图生命周期图:

【讨论】:

我经常看到 requestLayout 在调用 invalidate 之后被直接调用,我什至看到在 android 源代码中发生了类似 TextView 的事情,但根据这张图,这样做是多余的,对吧?那么这样做有什么目的吗? 嗯,这是一个有趣的问题,老实说,我真的不知道他们为什么在例如调用这两种方法。 TextView。我想也许他们想在更改其布局相关参数之前最后一次绘制View,但是如果我们考虑以不同的顺序调用这些调用(并且他们调用invalidate() 紧跟在 requestLayout() 之后,在 TextView 之后也是如此)。也许它值得在 *** 上提出另一个问题:)? (1/2):我认为你没有正确理解这两种方法(invalidate()requestLayout())。这些方法的目的是告诉View 发生了什么样的invalidation(如您所说)。这不像View 决定在您调用其中一种方法后要遵循的路径。选择View-lifecycle-path 背后的逻辑是选择正确的方法来调用自身。如果有与尺寸变化有关的东西——应该调用requestLayout(),如果只有视觉变化而没有尺寸变化——你应该调用invalidate() (2/2): 如果您以某种方式更改 View 的大小,例如您获取View 的当前LayoutParams 并修改它们,但NOT 调用requestLayoutsetLayoutParams(在内部调用requestLayout),然后你可以随意调用invalidate()View 不会经过测量布局过程,因此不会改变它的大小。如果您不告诉您的 View 它的大小已更改(使用 requestLayout 方法调用),那么 View 将假定它没有,并且 onMeasureonLayout 不会调用。 @tcox 更多在这里 --> ***.com/questions/35279374/…【参考方案2】:

invalidate()

当您想要安排重绘视图时,调用invalidate() 完成。这将导致 onDraw 最终被调用(很快,但不是立即)。自定义视图何时调用它的一个示例是文本或背景颜色属性发生更改时。

视图将被重绘,但大小不会改变。

requestLayout()

如果您的视图发生更改会影响大小,那么您应该致电requestLayout()。这将触发onMeasureonLayout 不仅针对此视图,而且一直针对父视图。

调用requestLayout() 是not guaranteed to result in an onDraw(与接受答案中的图表所暗示的相反),因此它通常与invalidate() 结合使用。

invalidate();
requestLayout();

这方面的一个例子是自定义标签的文本属性发生更改。标签会改变大小,因此需要重新测量和重新绘制。

forceLayout()

当在父视图组上调用requestLayout() 时,不需要重新测量和重新布局其子视图。但是,如果孩子应包括在重新测量和重新布局中,那么您可以在孩子身上拨打forceLayout()forceLayout() 仅适用于与直接父级上的 requestLayout() 一起出现的子级。单独调用forceLayout() 不会产生任何效果,因为它不会触发视图树上的requestLayout()

阅读this Q&A 了解forceLayout() 的更详细说明。

进一步研究

Creating a View Class: Add Properties and Events(有用的文档) View documentation View source code

【讨论】:

但是首先调用 requestLayout() 然后 invalidate() 不是更有意义吗? @scholt,据我所知顺序无关紧要,所以如果您愿意,可以在invalidate() 之前致电requestLayout()。两者都没有立即布局或绘制。相反,他们设置的标志最终会导致重新布局和重绘。【参考方案3】:

在这里你可以找到一些回应: http://developer.android.com/guide/topics/ui/how-android-draws.html

对我来说,调用invalidate() 只会刷新视图,调用requestLayout() 会刷新视图并计算屏幕上视图的大小。

【讨论】:

那么 forceLayout() 怎么样? @fiddler ,这个方法只设置了两个标志:PFLAG_FORCE_LAYOUT 和 PFLAG_INVALIDATED @suitianshi 那么设置标志的后果是什么? @Sergey 结果似乎是这个标志覆盖了测量缓存(视图使用缓存在未来更快地测量相同的MeasureSpec);在此处查看 View.java 的第 18783 行:github.com/android/platform_frameworks_base/blob/master/core/…【参考方案4】:

invalidate() ---> onDraw() 来自 UI 线程

postInvalidate() ---> onDraw() 来自后台线程

requestLayout() ---> onMeasure()onLayout() 并且 不一定 onDraw()

重要提示:调用此方法不会影响被调用类的子类。

forceLayout() ---> onMeasure()onLayout() 如果直接父级称为 requestLayout()

【讨论】:

以下内容不正确或应更好地解释。 “postInvalidate() ---> onDraw() 来自后台线程”。它在下一个 UI 线程绘制周期触发 onDraw。此外,您可以从后台线程调用 postInvalidate()。【参考方案5】:

您在要重绘的视图上使用 invalidate(),它将调用其 onDraw(Canvas c),并且 requestLayout() 将使整个布局渲染(测量阶段和定位阶段)再次运行。如果您在运行时更改子视图的大小,则应该使用它,但仅在特定情况下,例如来自父视图的约束(我的意思是父视图的高度或宽度是 WRAP_CONTENT ,因此匹配测量子视图,然后才能再次包装它们)

【讨论】:

【参考方案6】:

This answer 对 forceLayout() 不正确。

正如您在the code of forceLayout() 中看到的那样,它只是将视图标记为“需要重新布局”,但它既不安排也不触发重新布局。直到将来某个时候视图的父级由于其他原因被布局时才会发生重新布局。

使用forceLayout()requestLayout() 时还有一个更大的问题:

假设您在视图中调用了forceLayout()。现在,当在该视图的后代上调用 requestLayout() 时,Android 将递归地在该后代的祖先上调用 requestLayout()。问题是它会在您调用forceLayout() 的视图上停止递归。 所以requestLayout() 调用永远不会到达视图根,因此永远不会安排布局传递。 视图层次结构的整个子树正在等待布局并在该子树的任何视图上调用 requestLayout()不会造成布局。只有在该子树之外的任何视图上调用 requestLayout() 才能打破咒语。

我会考虑 forceLayout() 的实现(以及它如何影响 requestLayout() 被破坏,你不应该在你的代码中使用该函数。

【讨论】:

嗨!你是怎么想出那个咒语的?这在某处有记录吗? 不幸的是,它没有记录在案,甚至可能不是有意的。我通过调查View 的代码并自己遇到问题并对其进行调试来解决这个问题。 然后我很好奇forceLayout() API 的用途:它实际上并不强制布局通过,而只是更改标志which is being observed in onMeasure(),但onMeasure() 不会除非调用requestLayout() 或显式View#measure(),否则调用。这意味着,forceLayout() 应该与requestLayout() 配对。另一方面,如果我还需要执行requestLayout(),为什么还要执行forceLayout() @azizbekian 不,你不必这样做。 requestLayout() 做了所有forceLayout() 也做的事情。 @Suragch 错过了那个,谢谢!非常有趣的分析。 forceLayout 的预期用途是有意义的。所以最后它只是非常糟糕的命名和记录。

以上是关于forceLayout()、requestLayout() 和 invalidate() 的使用的主要内容,如果未能解决你的问题,请参考以下文章

D3 强制布局 - 按名称而不是索引链接节点

D3强制布局:如何设置每个节点的大小?

使用 ConstraintSet 时测量的布局大小为零

Android ListView scrollby 剪辑列表中的项目

避免 D3 强制布局中节点和边之间的碰撞

在版本 4 中将节点动态添加到 D3 Force Layout