forceLayout(),requestLayout()和invalidate()的用法


Answers:


357

为了更好地理解FrançoisBOURLIEUXDalvik提供的答案,我建议您看一下Arpit Mathur的这张令人敬畏的视图生命周期图: 在此处输入图片说明


28
我经常看到在调用invalidate之后直接调用requestLayout,甚至看到Android源代码中发生了诸如TextView之类的事情,但是根据此图,这样做是多余的,对吗?那么这样做有什么目的吗?
tcox 2015年

5
嗯,这是一个有趣的问题,老实说,我真的不知道为什么在eg中同时调用这两种方法TextView。我想,也许他们想得出View最后一次他们改变其布局相关参数之前,但它并没有真正使任何意义,如果我们想在不同的顺序被调用这些电话(他们称之为invalidate()后立即requestLayout()TextView以及)。也许值得在StackOverflow上提出另一个问题:)?
Bartek Lipinski

14
(1/2):我认为您无法正确理解这两种方法(invalidate()requestLayout())。这些方法的目的是告诉您发生了View什么样的失效(如您所说的)。View调用这些方法之一后,并不能决定要遵循的路径。选择View-lifecycle-path 的逻辑是选择适当的方法来调用自身。如果存在与尺寸更改有关的某些内容- requestLayout()应该调用,如果只有视觉更改而尺寸没有更改-您应该调用invalidate()
Bartek Lipinski

11
(2/2):如果您View以某种方式更改大小,例如,获得a的当前LayoutParamsView并对其进行修改,但调用requestLayoutsetLayoutParamsrequestLayout内部调用),则可以随意调用invalidate(),并且该View不会通过测量布局过程中,因此不会改变它的大小。如果您不告诉您View它的大小已更改(使用requestLayout方法调用),则Viewwill将假定它没有更改,并且onMeasureand onLayout将不会被调用。
Bartek Lipinski


125

invalidate()

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

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

requestLayout()

如果关于视图的某些更改会影响大小,则应致电requestLayout()。这将触发onMeasureonLayout不仅是这个观点,但一路上扬父视图的线。

不能保证调用requestLayout()导致onDraw(与接受的答案中的图相反),因此通常将其与结合使用invalidate()

invalidate();
requestLayout();

例如,自定义标签的text属性发生更改时。标签会改变尺寸,因此需要重新测量和重新绘制。

forceLayout()

requestLayout()父视图组上调用时,不需要重新测量并重传其子视图。但是,如果在重新测量和重排中应包括一个孩子,则可以呼叫forceLayout()该孩子。forceLayout()仅在子代requestLayout()与其直接父代一起出现时才对子代起作用。forceLayout()本身调用不会产生任何效果,因为它不会触发requestLayout()视图树。

阅读此问答以获取有关以下内容的详细说明forceLayout()

进一步研究


1
但是首先调用requestLayout()然后使invalidate()更有意义吗?
scholt

2
@scholt,据我所知顺序并不重要,因此您可以requestLayout()invalidate()需要时致电。没有人做布局或立即绘制。相反,它们设置标志,最终将导致重新布局并重画。
Suragch

27

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

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


3
那么forceLayout()呢?
sdabet

@fiddler,此方法仅设置两个标志:PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED
Suitianshi 2014年

7
@suitianshi那么设置标志的后果是什么?
谢尔盖(Sergey)2015年

1
@Sergey结果似乎是此标志将覆盖度量缓存(视图使用缓存在以后对同一个MeasureSpec进行更快的度量);参见View.java的第18783行:github.com/android/platform_frameworks_base/blob/master/core/…–
RhetoricalRuvim

3

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


3

这个答案是不正确的forceLayout()

如您在代码中所见forceLayout()它仅将视图标记为“需要重新布局”,但是它既不计划也不触发该重新布局。重新布局将不会发生,直到将来某个时候由于其他原因布置视图的父级。

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

假设您已经调用forceLayout()了一个视图。现在,当调用requestLayout()该视图的后代时,Android将递归调用requestLayout()该后代的祖先。问题在于它将在您调用的视图处停止递归forceLayout()因此,该requestLayout()调用将永远不会到达视图根,因此也不会调度布局遍历。视图层次结构的整个子树都在等待布局,并且调用requestLayout()该子树的任何视图都不会导致布局。仅requestLayout()在该子树之外调用任何视图都会破坏该咒语。

我会考虑的实现forceLayout()(以及它如何requestLayout()被破坏,因此您永远不要在代码中使用该函数。


1
嗨!你是怎么想到那个咒语的?那是记录在某处吗?
azizbekian

1
不幸的是,它没有记录在案,甚至可能不是故意的。我通过调查代码View并亲自遇到问题并对其进行调试来解决了。
fluidsonic

然后,我对forceLayout()API 的用途感到好奇:它实际上并没有强制执行布局传递,而只是更改了在中观察到onMeasure()的标志,但是onMeasure()除非被调用requestLayout()或显式调用,否则它不会被View#measure()调用。也就是说,forceLayout()应该与配对requestLayout()。另一方面,forceLayout()如果仍然需要执行,为什么还要执行requestLayout()呢?
azizbekian

@azizbekian不,您不必。requestLayout()做所有forceLayout()也做的事情。
fluidsonic

1
@Suragch错过了那个,谢谢!非常有趣的分析。的预期用途是forceLayout有道理的。因此,最后它的命名和记录都非常糟糕。
Fluidsonic

0

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

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

requestLayout()---> onMeasure()onLayout()AND 不一定 onDraw()

  • 重要说明:调用此方法不会影响被调用类的子级。

forceLayout()---> onMeasure()onLayout() JUST如果直接父级调用requestLayout()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.