为什么invalidate后直接调用requestLayout

Why is requestLayout being called directly after invalidate

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

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

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

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

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

另一方面,

requestLayout() 几乎完成了从测量到重新渲染的所有工作。

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

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

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

如果您没有对视图执行任何会改变其大小的操作,则不必调用 requestLayout().

如果您返回并查看 TextView 代码中调用 requestLayout() 的地方,就会发现视图边界会受到影响的方法。例如setPadding()setTypeface()setCompoundDrawables()

因此,当 requestLayout() 被调用时,它应该与无效调用配对以确保重绘整个视图。

Android docs: Creating a View class

public boolean isShowText() {
   return mShowText;
}

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

Notice that setShowText calls invalidate() and requestLayout(). These calls are crucial to ensure that the view behaves reliably. You have to invalidate the view after any change to its properties that might change its appearance, so that the system knows that it needs to be redrawn. Likewise, you need to request a new layout if a property changes that might affect the size or shape of the view. Forgetting these method calls can cause hard-to-find bugs.

本书 Expert Android 中回答问题的相关摘录:

Because the onClick event has caused the dimensions to change, our view needs to become bigger and take more space. How do we express that need to Android, Well, we request Layout(). This method goes up the chain, marking every view parent that it needs to be remeasured. When the final parent gets this request (the view root), the parent schedules a layout traversal. A layout traversal may or may not result in onDraw, although in this case it should. As a good programming practice, we also call invalidate() to ensure the drawing phase as well.

看到the following diagram后,我的印象是调用requestLayout()最终会导致onDraw

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

invalidate();
requestLayout();

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

我的 source (thanks to ) is the Romain Guy(他是 Google 的 Android 工程师):

requestLayout() itself does not lead to a draw pass but some views might react to a Layout change by calling invalidate.

因此,要确定重新布局会导致重绘,那么您应该将 invalidate()requestLayout() 配对。 (但反之则不然。如果您只需要重绘,则无需调用 requestLayout()。一个 invalidate() 即可。)