一些常用的android布局如 RelativeLayout和LinearLayout(当权重非零时) 有onMeasure()实现来衡量他们的孩子 两次,导致嵌套时的指数运行时间。 通过发出日志条目可以轻松验证这一点 从叶子视图的onMeasure()...它被称为2 ^深度 次。
有人可以清楚具体地说明为什么会这样吗? 最重要的是,这是指数行为的重要组成部分 整体合同,还是仅仅是一个实施细节 那可能会被优化掉?如果认为这是不可避免的, 请举一个需要它的例子。
这样的例子对我和其他人有很大的帮助 谁抱怨“保持你的布局浅薄” 任务是繁重的,谁在想 这是否仅仅是由于 核心库中尚未优化的算法, 或者是否确实存在根本性的困难 阻止修复。
也许最小的例子就是 在另一个LinearLayout内的LinearLayout内的Button (在任何地方使用match_parent和weight = 1来触发 完全指数行为), 带有一些额外的参数或情况 明确表示所有四个电话 到Button.onMeasure()确实是有意义和必要的。
我的第一个猜测是只有两个线性时间 真的需要遍历 - 第一次收集每个人的遍历 首选尺寸,第二次遍历分配松弛 和/或收缩。 世界上其他布局引擎,如Tex和Swing 似乎能够经常处理非常深层次的层次结构 有很多对齐约束和延伸, 没有任何指数爆炸,我想这就是他们的工作方式。
请注意,我不希望答案解释如何 指数爆炸发生 - 我理解, 已经有过几个帖子 问道并回答:
我的问题是递归双重测量是否是 从根本上说是必要的/合理的, 如果是的话,我想要一个明确的解释/示例来说明原因。
编辑2013/8/22:我想也许我的问题仍未解决。 我会尝试澄清和解释我的动机,这次更大胆。
布局不是一个指数级的难题, 正如Tex和Swing等世界上有效的布局引擎所证明的那样。
那么,LinearLayout发生了什么, android开发者社区应如何进行响应? 我不是本着责备的精神问, 而是要了解并决定如何最好地向前发展。
我可以想到4种可能性:
(4)对我个人来说不是一个严肃的选择。 此外,我似乎很清楚 此时更改LinearLayout的行为是不切实际的, 所以我不相信(2)也是一个严肃的选择。
离开(1)和(3)。 我有能力并愿意亲自去做其中任何一个,但哪一个? 显然(1)如果可能的话,这是可取的 - 所以,有可能吗? 这似乎是需要回答的关键阻塞问题 为了确定如何前进。
我在核心代码中花了一些时间 和文件,它并没有变得清晰, 所以这就是我在这里问这个问题的原因。
答案 0 :(得分:5)
就两次测量孩子而言,我的理解是这就是LinearLayouts所发生的情况,特别是涉及权重时。我发现的最好的解释来自RomainGuy在他的一个演讲中。
他对此有一个幻灯片,并在17:45简要地说出来。尽管如此,请随意回放以获得一些背景信息。您可以在此处找到我引用的视频:Devoxx'10 - Dive Into Android
基本上他说的是,在第一遍时,他们根据LinearLayout的方向计算总宽度或高度,添加子项的权重,并找出剩余多少空间,然后在第二遍他们能够正确地将所有剩余空间分配给所有孩子。很简单。
我也想指出,虽然是的,但是浅层布局层次结构的性能影响较小,如果你只添加1或2个额外的层,你可能不会#39;将会对用户产生巨大的性能影响。一旦它布局完成,它就完成了。即使在ListView中,如果您正确使用给定的" convertView",并设置ViewHolder,您将获得良好的性能。
我鼓励您使用DDMS并对Google的某些应用进行布局转储。它们非常复杂,而且往往非常复杂,但它们仍然可以获得良好的性能。不要对你的布局愚蠢,但如果它节省你的时间,增加一个额外的布局不是世界的尽头。
答案 1 :(得分:2)
从这里开始:http://developer.android.com/guide/topics/ui/how-android-draws.html
父视图可能会对其子项多次调用measure()。例如,父母可以用未指定的维度测量每个孩子一次以找出他们想要的大小,然后如果所有孩子的无约束大小的总和太大或太小,则用实际数字再次对他们调用measure()(也就是说,如果孩子们不同意他们每人得到多少空间,那么父母就会在第二次通过时进行干预和制定规则。
他们似乎将测量传递视为父和子之间的对话。换句话说,他们选择了最大的灵活性而不是优化。看起来他们仍然可以优化基本布局。
答案 2 :(得分:2)
考虑这个例子:
红色框表示垂直方向的LinearLayout
。绿色框表示水平方向的另一个LinearLayout
。我希望布局能够像这样进行:
1)红色LinearLayout
将测量绿色LinearLayout
的高度和底部的大蓝色小部件,然后比较它们的权重,相应地划分任何剩余的垂直空间,并再次测量孩子。 (这里要知道的重要一点是“测量”视图实际上设置它的大小。)
2)绿色LinearLayout
然后测量其三个孩子的宽度,比较它们的重量,划分水平空间,再次“测量”。
要注意的是,为了使红色布局测量绿色布局的高度,绿色布局需要知道其子项的高度。
现在,你会认为LinearLayout
足够聪明,可以优化掉它的许多计算。例如,在确定自己的高度时,绿色布局没有逻辑上的原因来测量其子项的 width 。如果它的孩子都有fill_parent
的高度,那么它根本不需要执行任何计算。
但API不允许LinearLayout
那么聪明。 根本问题在于,无法仅测量视图的一个维度。您可以在测量后单独获取,但实际测量已完成由View#onMeasure(int, int)。该方法的参数使用View.MeasureSpec进行编码,并且无法对“忽略此维度”进行编码。因此,当红色布局测量时,绿色LinearLayout
愚蠢地计算其所有子项的两个维度,然后在它自己的布局时再次重复整个过程。第二次宽度不会改变,但它们仍然必须重新计算,因为再次说明没有办法告诉布局或它的子节点只测量一个维度。
所以回答你的问题......不,它不一定需要这样,至少对于许多常见的用例。这是Android API的缺陷。
如果Google要向View
添加新方法以分别衡量维度,则默认实施必须依赖onMeasure(int, int)
,这可能会更糟糕。但如果View.MeasureSpec
编码中有任何未使用的位,则可能会添加“忽略”标记,以便更好地优化LinearLayout
的未来版本。
答案 3 :(得分:0)
我认为问题是测量缓存。据我所知,只有在中间有一个视图布局时,缓存才有效,所以当你在同一个“onMeasure”中执行两个连续测量的孩子(即使具有相同的测量规格)所有孩子他们的子女再次被测量。如果度量缓存工作正常,则第二个度量应该更快,因为它将采用缓存值,并且不会导致再次测量所有子层次结构。