为什么在调用invalidate后直接调用requestLayout?

14

我正在学习自定义视图,想了解 invalidate()requestLayout()

请参考此答案和其图表:

https://dev59.com/eGYr5IYBdhLWcg3wOXsQ#25846243

invalidate() 告诉Android视图的状态已经改变,需要重新绘制。

requestLayout() 意味着视图的大小可能已经改变,需要重新测量,然后重新绘制。

invalidate() 将调用 dispatchDraw()draw()onDraw(),因此会重新渲染视图。

另一方面,requestLayout() 几乎需要重新进行从测量到重新渲染的所有操作。

为什么很多例子(甚至是 TextView 的源代码)都会在下一行立即调用 invalidate()requestLayout()

4个回答

17
invalidate() 专门用于重新绘制视图的内容。重新绘制不会同步进行,而是将视图的区域标记为无效,以便在下一个渲染周期中重新绘制。 requestLayout() 应该用于当其中的某些内容可能已更改其尺寸时。在这种情况下,父视图和所有其他父级视图都需要通过布局来调整自己。
如果您没有对视图进行任何会更改其大小的操作,则不必调用 requestLayout()
如果您回头查看 TextView 中调用 requestLayout() 的代码位置,它将出现在影响视图边界的方法上。例如,setPadding()setTypeface()setCompoundDrawables() 等。
因此,在调用 requestLayout() 时,应与调用 invalidate 配对,以确保重新绘制整个视图。

感谢您的详细解释。invalidate() 和 requestLayout() 一起被调用以重新绘制我们的视图,并让包含我们视图的父视图组也能够适应我们重新绘制时可能发生的大小变化。 - Ersen Osman
2
我有点不清楚为什么在调用requestLayout()时还要调用invalidate()。似乎requestLayout()总是会导致整个视图被重绘;此外再调用invalidate()似乎是多余的。 - pathfinderelite
3
“我必须调用invalidate()函数来呈现新的文本,以便它显示。” 我的观点是,这个陈述并不完全正确;仅调用requestLayout()(而不使用invalidate())也可以呈现新的文本。因此,我的问题是,在需要调用requestLayout()的情况下,为什么还要调用invalidate()呢? - pathfinderelite
1
@ErsenOsman 我和pathfinderelite有同样的问题。我认为这里的困惑是,requestLayout()会在onMeasure()和onLayout()之后提示调用onDraw()吗?如果是这样,那么为什么我们需要在requestLayout()之后调用invalidate()呢? - boxme
1
@ErsenOsman requestLayout() 不会触发 onDraw()。所以你是正确的。链接:http://grokbase.com/p/gg/android-developers/12amz5pqv8/does-an-ondraw-get-called-if-i-dont-do-invalidate-but-did-the-requestlayout - boxme
显示剩余6条评论

15
看了下面的图表之后,我认为调用requestLayout()最终会导致onDraw被调用。

enter image description here

因此,没有必要将它们一起调用,因为这是多余的。
invalidate();
requestLayout();

然而,事实证明该图表是误导性的。当布局发生变化时,一些视图可能会使自身无效,但这并不是确定的。调用requestLayout()不能保证会导致onDraw被调用。

我的来源(感谢此评论)是Romain Guy(他是Google的Android工程师):

requestLayout()本身并不会导致绘制过程,但一些视图可能会通过调用invalidate来响应布局更改。

因此,为了确保重新布局会导致重绘,请将invalidate()requestLayout()配对使用。(反之则不成立。如果您只需要重绘,则无需调用requestLayout()。单个invalidate()即可。)

这篇文章是在2012年写的。在2018年,语句“调用requestLayout()不能保证会导致onDraw被调用”是否仍然有效? - Johnny Five
@JohnnyFive,我没有看到任何源代码或文档的更新,表明情况有所改变。因此,是的,它仍然有效。 - Suragch

2

以下是来自Expert Android的相关摘录,回答了这个问题:

由于onClick事件导致尺寸发生变化,我们的视图需要变得更大并占用更多空间。我们如何向Android表达这种需求呢?我们请求Layout()方法。该方法向上遍历整个视图树,标记每个视图父级需要重新测量。当最终的父级(即视图根)收到此请求时,它会安排布局遍历。布局遍历可能会或可能不会导致onDraw调用,但在这种情况下应该会。作为良好的编程实践,我们还调用invalidate()方法以确保绘制阶段也能够正常进行。


0

Android文档:创建一个View类

public boolean isShowText() {
   return mShowText;
}

public void setShowText(boolean showText) {
   mShowText = showText;
   invalidate();
   requestLayout();
}

请注意,setShowText调用invalidate()和requestLayout()。这些调用对于确保视图的可靠行为至关重要。在更改可能会改变视图外观的任何属性后,您必须使视图无效,以便系统知道需要重新绘制它。同样,如果更改可能影响视图的大小或形状,则需要请求新布局。忘记这些方法调用可能会导致难以找到的错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接