Android的布局是否真的很难?

一些常用的android布局,如RelativeLayout和LinearLayout(当权重不为零时)有onMeasure()实现来测量他们的孩子两次,导致嵌套时的指数运行时间。 这很容易通过从叶子View的onMeasure()发出日志条目来validation…它被称为2 ^深度时间。

有人可以给出一个清楚而具体的描述为什么这是? 最重要的是,这种指数行为是由于整个合同的一个重要部分,还是仅仅是一个可能被优化掉的实现细节? 如果认为这是不可避免的,请举个例子说明。

这样一个例子会大大地帮助我和其他人抱怨,“保持你的布局浅”的任务是繁重的,谁在想这是否是由核心库中的不是最优化algorithm驱动的,或者是否真的存在阻止修复的根本难点。

也许一个最小的例子将包含另一个LinearLayout中的LinearLayout中的一个Button(在每个LinearLayout中都带有match_parent和weight = 1,以触发完整的指数行为),以及一些其他参数或情况,使其清楚地表明所有四个对Button的调用。 onMeasure()确实是有意义和必要的。

我的第一个猜测是,只有两个线性时间遍历是真正需要的 – 第一个遍历每个人的首选大小,第二遍遍分配松弛和/或收缩。 像Tex和Swing这样的世界上的其他布局引擎似乎能够经常处理具有大量alignment约束和延伸的非常深层次的层次结构,而没有任何指数性的爆炸,我想象它们是如何工作的。

请注意,我不想要解释如何发生指数性爆炸的答案 – 我知道,并且已经有几个post已经被提出并回答了:

  • 为什么嵌套权重对性能不利? 备择scheme?
  • Android布局测量时间会随着层次结构的每一步骤而加倍
  • 布局重量警告嵌套重量performance不佳
  • Android布局层次结构的效率
  • http://android-developers.blogspot.com/2009/02/android-layout-tricks-1.html

我的问题是,recursion双重测量是否是根本上必要/合理的,如果是这样,我想要一个清楚的解释/例子来说明原因。

编辑2013/8/22:我想也许我的问题还没有得到解决。 我会尽量澄清和解释我的动机,这次更大胆。

布局不是一个指数级的难题,正如Tex和Swing这样的世界上有效的布局引擎所certificate的。

那么,LinearLayout发生了什么,android开发者社区应该如何继续呢? 我不是本着责怪的精神,而是要理解和决定如何最好地前进。

我能想到4个可能性:

  1. 修复核心库中的性能错误,而不更改任何合同
  2. 根据需要更改合同,并修复核心库中的性能错误
  3. 写一个LinearLayout的替代scheme,它具有其基本特征(即按指定比例在儿童之间分配额外的空间),但没有性能错误,并将其用于新的应用程序
  4. 继续对我们的布局进行微观pipe理,以解决我们其余Android开发事业的性能问题。

(4)对我个人来说不是一个严肃的select。 此外,在我看来,在这一点上改变LinearLayout的行为是不切实际的,所以我不认为(2)也是一个严肃的select。

这留下(1)和(3)。 我有能力也愿意亲自去做,但是哪一个呢? 显然(1)如果可能的话是远远可取的 – 那么这是可能的吗? 这似乎是需要回答以确定如何前进的关键阻塞问题。

我已经花了一些时间在核心代码和文档,它不是很清楚,所以我在这里问这个问题。

在对孩子进行两次测量方面,我的理解是LinearLayouts会发生这种情况,尤其是在涉及权重时。 我发现的最好的解释来自RomainGuy在他的一个演讲中。

他对此有一个幻灯片,并在17:45简单地谈到这一点。 尽pipe如此,您可以随意倒带以获得一些背景。 你可以在这里find我参考的video: Devoxx'10 – Dive Into Android

基本上他说的是,第一遍他们根据LinearLayout的方向计算总的宽度或者高度,添加孩子的权重,并且找出剩下多less空间,然后在第二次传递这些信息的时候,能够把所有的剩余空间都分配给所有的孩子。 很简单。

我还想指出的是,虽然浅层布局层次结构对性能的影响不大,但如果只增加1层或2层,则可能不会对性能产生重大影响为用户。 一旦布置完成,就完成了。 即使在ListView中,如果你正确地使用给定的“convertView”,并设置ViewHolder的,你会得到很好的性能。

我鼓励你使用DDMS,并做一些谷歌的应用程序的布局转储。 他们是非常复杂的,往往时间惊人的深刻,但他们仍然取得了良好的业绩。 不要愚蠢的布局,但如果它节省您的时间,增加一个额外的布局不是世界的尽头。

从这里: http : //developer.android.com/guide/topics/ui/how-android-draws.html

一个父视图可以多次调用它的子对象measure()。 例如,父母可以用未指定的维度来测量每个孩子一次,以找出他们想要的大小,如果所有孩子的不受约束的大小之和太大或太小,就用实际数字再次调用measure()也就是说,如果孩子们不同意他们各自得到多less空间,那么父母会介入,并在第二遍设置规则。

他们似乎将测量通过视为父母与子女之间的对话。 换句话说,他们select了最大的灵活性,而不是优化。 它似乎仍然可以优化基本布局。

我今晚来这里问这个。 这似乎令人失望,甚至似乎没有人能理解你的问题。 考虑了几个小时之后,我想我可能会知道答案。

考虑这个例子:

在这里输入图像说明

红色框表示垂直方向的LinearLayout 。 绿色框表示另一个水平方向的LinearLayout 。 我希望布局继续这样下去:

1)红色LinearLayout将测量绿色LinearLayout的高度和底部的蓝色小部件的高度,然后比较它们的权重,相应地分割剩余的垂直空间,然后再次测量小孩。 (这里要知道的重要一点是,“测量”一个视图实际上确定了它的大小。)

2)然后,绿色的LinearLayout将测量三个孩子的宽度,比较它们的权重,划分水平空间,然后再次“测量”。

值得一提的是,为了让红色布局能够衡量绿色布局的高度,绿色布局需要知道孩子的身高。

现在,您可能会认为LinearLayout足够聪明,可以优化其大部分计算。 例如,在确定自己的身高时,绿色布局没有合理的原因来衡量孩子的宽度 。 如果它的孩子都有一个fill_parent的高度,那么它根本就不需要进行任何计算。

但API不允许LinearLayout那么聪明。 根本的问题是,没有办法衡量一个观点的一个维度。 测量完成后,可以单独获取它们,但实际测量是通过View#onMeasure(int,int)完成的 。 该方法的参数使用View.MeasureSpec进行编码,并且无法编码“忽略此维度”。 因此,绿色的LinearLayout愚蠢地计算红色布局测量它的所有孩子的两个维度,然后重复整个过程,当它做自己的布局。 第二次宽度不会改变,但仍然需要重新计算宽度,因为再次无法告诉布局或其子只测量一个维度。

所以要回答你的问题…不,不一定需要这样,至less对于许多常见的使用情况。 这是Android API的一个缺陷。

如果Google将新方法添加到View来分别衡量维度,则默认实现将不得不依赖于onMeasure(int, int) ,这可能会对性能造成更大的影响。 但是,如果View.MeasureSpec编码中有任何未使用的位,则可以添加一个“忽略”标志,以便将来版本的LinearLayout得到更好的优化。