14

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

请参考这个答案及其图表:

https://stackoverflow.com/a/25846243/4243687

invalidate()告诉 Android 视图的状态发生了变化,需要重新绘制。

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

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

requestLayout()另一方面,几乎可以完成从测量到重新渲染的所有工作。

为什么有这么多示例(甚至是TextView源代码)调用invalidate()然后requestLayout()就在下一行?

4

4 回答 4

17

invalidate()专门用于重绘视图的内容。重绘不会同步发生。相反,它将您的视图区域标记为无效,以便在下一个渲染周期中重新绘制它。

requestLayout()当其中的某些东西可能改变了它的尺寸时,应该使用它。在这种情况下,父视图和视图层次结构中的所有其他父视图将需要通过布局传递重新调整自己。

如果您没有对您的视图做任何会改变其大小的事情,那么您不必调用requestLayout().

如果您回过头来查看代码TextViewrequestLayout()调用的位置,则会发现视图边界会受到影响的方法。例如,setPadding()setTypeface()setCompoundDrawables()等。

所以,当requestLayout()被调用时,应该与调用invalidate配对,以确保整个视图被重绘。

于 2016-02-09T00:27:17.347 回答
14

看到下图后,我的印象是调用requestLayout()最终会导致onDraw.

在此处输入图像描述

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

invalidate();
requestLayout();

然而,事实证明,该图具有误导性。当布局发生变化时,某些视图实际上可能会使自己失效,但这并不确定。不保证调用requestLayout()会导致onDraw被调用。

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

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

因此,要确定重新布局会导致重画,那么您应该将 aninvalidate()requestLayout(). (但是,相反的情况并非如此。如果您只需要重绘,则无需调用requestLayout()。一个invalidate()就可以了。)

于 2017-02-24T03:03:00.490 回答
2

Expert Android一书中的相关摘录回答了这个问题:

因为onClick事件导致尺寸发生变化,我们的view需要变大,占用更多空间。我们如何向 Android 表达这种需求,好吧,我们请求 Layout()。此方法沿链向上,标记需要重新测量的每个视图父级。当最终的父级收到此请求(视图根)时,父级会安排一次布局遍历。布局遍历可能会或可能不会导致 onDraw,尽管在这种情况下它应该。作为一种良好的编程习惯,我们还调用 invalidate() 来确保绘图阶段。

于 2016-11-26T07:15:53.740 回答
0

Android 文档:创建视图类

public boolean isShowText() {
   return mShowText;
}

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

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

于 2016-11-03T13:07:42.670 回答