【问题标题】:Performance: How to prevent requestLayout() from laying out entire hierarchy性能:如何防止 requestLayout() 布局整个层次结构
【发布时间】:2013-01-15 11:28:42
【问题描述】:

我有一个相当复杂的安卓应用程序。已经尽可能扁平化视图层次结构,但我仍然在应用程序中存在滞后。例如,有一个菜单,其中的条目通过ValueAnimator 设置其高度来折叠/展开。通常,动画第一次运行时会有一点延迟,而在第一次运行之后就很流畅了。

我注意到,当我在 Menu-Item 上调用“requestLayout()”时,Android 似乎在整个层次结构中执行布局传递和多个度量传递。

  • 因为我知道虽然 Menu-Item(View) 改变了高度,但 Menu(View) 本身没有改变,有没有办法告诉应用程序?
  • 我能否以某种方式执行这似乎滞后于自己的第一次传递,以便它发生在应用程序启动之后而不是在第一次触摸输入时?

这是我正在制作的动画的草图:

【问题讨论】:

  • 我也有同样的问题。对我来说更烦人的是,我有一个带有自定义视图图标的复杂列表。当一个小文本窗口在其下方更新(每 1 个性别)时,将重绘所有列表。这会带来短暂的延迟。
  • @ArtursVancans 嘿,arturs,我也遇到了这个问题。如果您能分享一下如何摆脱这个问题,将非常感谢。
  • 查看 React-Native 或 anko 之类的库。

标签: android performance layout


【解决方案1】:

我不确定为什么会在您的动画中触发布局,但我将抽象地回答您的问题。

如果您在动画中调用 requestLayout(直接或间接),那么您做错了。

为了正确性和安全性,requestLayout 对视图层次结构进行了完整的视图遍历,b/c 从概念上改变视图层次结构中节点的边界框可能会导致任何其他节点的边界发生变化。并非总是如此,但通常它可以,这就是为什么 requestLayout 是一个完整的遍历。

所有这些只是另一种说法,requestLayout 会占用 16.6 毫秒帧时隙的时间,让你的动画变得断断续续。这对于具有许多相对布局的深层和复杂层次结构尤其不利,这些结构内部每个级别执行两次传递(因此可能导致子树上的指数传递)

现在,如果您想为尺寸变化设置动画,请在硬件层中使用 setScale。并在动画结束时愉快地调用 requestlayout 并销毁图层(以释放内存)。

因为它是一个层,所以在动画中重复调用 setScale 会导致 GPU 上的纹理发生变化,从而完全绕过视图层次结构的遍历机制。这应该使它像黄油一样光滑。

【讨论】:

  • 一个布局被触发,因为我正在设置一个RelativeLayout的高度。我希望它从全高折叠到只显示它包含的最上面的视图。
  • 动画现在运行流畅,我就是有这个问题:stackoverflow.com/questions/17089548/…(第一次运行滞后,之后每次运行流畅)
  • 给你的另一个问题:我现在知道布局过程是如何工作的,但是真的没有办法告诉android布局的一部分没有改变,所以它不需要至少重新测量它们?在我的应用中,测量部分是最昂贵的。
  • 图片帮助很大。更改 LayoutParams 将导致 requestLayout 因此在动画中重复这样做是不好的。这个解决方案怎么样 - 如果框折叠到相同的高度,您将除第一行之外的所有内容放在 Framelayout 中,并将其可见性更改为不可见/消失(对其进行动画处理)。如果您为外盒设置了 LayoutTransition,容器会自动为您设置尺寸动画。
  • 关于在动画期间缓存测量值,这是一个很好的优化。 setMeasure 像 onLayout 是递归的。您始终可以避免递归子树(通过 measureChildren),直接调用 setMeasureDimensions。
【解决方案2】:

你的问题看起来像我的:Only relayout children and not all the tree

首先,您可以尝试避免视图的复杂视图层次结构。如果可能,请分解不依赖于另一个视图的视图。

执行动画时,避免任何布局请求。如果布局请求未决,则延迟开始动画。

如果可能的话,使用硬件层来制作动画(也许 Android 默认使用 ValueAnimator

【讨论】:

  • 我使我的视图层次结构尽可能平坦,而不必合并大量荒谬的代码。硬件加速是存在的,尽管需要进行大量的错误试验才能找出在哪些层上启用或禁用它以获得预期的结果。
  • 如何检测布局请求是否处于待处理状态?
  • 据我所知,你不能。系统本身可以请求布局。不要在动画之前调用 addView()、removeView() 或类似的东西。如果您无法避免这种情况,例如使用 post() 或处理程序以 100 毫秒延迟开始动画,它会有所帮助。
  • 如果你的 UI 太深或太复杂,尝试绘制自定义视图,而不是使用一些布局和视图
  • 是的,但这让我的代码非常臃肿。而且我讨厌我不得不从 Android 中丢弃这么多东西并用我自己的代码替换它。
猜你喜欢
  • 2015-05-17
  • 2012-06-18
  • 1970-01-01
  • 2021-07-07
  • 1970-01-01
  • 1970-01-01
  • 2012-10-04
  • 1970-01-01
  • 2019-02-28
相关资源
最近更新 更多