为啥我们不应该将每个包含的 Android XML 布局包装在一个 <merge> 对中?
Posted
技术标签:
【中文标题】为啥我们不应该将每个包含的 Android XML 布局包装在一个 <merge> 对中?【英文标题】:Why shouldn't we wrap every included Android XML layout in a <merge> pair?为什么我们不应该将每个包含的 Android XML 布局包装在一个 <merge> 对中? 【发布时间】:2012-09-13 03:54:42 【问题描述】:作为question 的后续行动,我想不出有什么理由不应该将每个包含的 XML 布局包装在 <merge>
对中。
这让我想知道,为什么 ADT 团队不将其设为默认行为?
是否存在不希望这种行为的情况?
顺便说一句,<merge>
标签的android documentation 中的解释比最糟糕的法律协议中的措辞还要糟糕:
<merge />
标签有助于消除视图中的冗余视图组 将一个布局包含在另一个布局中时的层次结构。例如,如果 您的主要布局是垂直的LinearLayout
,其中两个连续 视图可以在多个布局中重复使用,那么可重复使用的布局在 你放置的两个视图需要它自己的根视图。然而, 使用另一个LinearLayout
作为可重用布局的根 在垂直LinearLayout
内产生垂直LinearLayout
。这 嵌套的LinearLayout
除了放慢速度之外没有任何实际用途 您的 UI 性能。
罗曼,你在哪里?
【问题讨论】:
如果您不确定父视图是什么会发生什么?您将如何为您的孩子设计风格? @hwrdprkns 我不确定我是否理解你。你能举个例子吗?这听起来像是问题/评论形式的答案。通过“样式”,我假设您的意思是“决定它们应该包含在什么类型的布局中”? 呃,对不起,我不清楚。我的意思是merge
在您不知道父 ViewGroup
是什么时没有用,因为合并不会/不能在不同的视图组之间合并。例如将 LinearLayout
和 FrameLayout
合并是没用的。
@hwrdprkns 也许可以,但是有什么害处吗?我认为这个标签的主要原因是防止你需要用任何类型的ViewGroup
包围包含的布局文件。即使您确实在包含文件的LinearLayout
中包裹了一个包含<merge/>
对的包含LinearLayout
,我认为它仍然会将内部LinearLayout
包含在外部文件中,对吧?
如果封闭和封闭的 ViewGroup 元素对子元素应用相同的布局参数,那么是的,自动合并将很有用。但是谁知道有些人可能已经/从代码中寻找对封闭 ViewGroup 的引用。
【参考方案1】:
include
标签的主要目的(我认为)是允许开发人员创建可重用的 xml 组件,以便在同一个活动或/和应用程序中的许多活动中多次使用。为了使该可重用组件真正有用,它需要是自包含的,并且尽可能减少外部连接。从我的角度来看,如果您要在每个包含的布局中使用 merge
标签,这将降低 include
标签的整体实用性。这是我认为会发生这种情况的一个例子:
考虑您想要实现一个可重用的ActionBar
xml 组件以嵌入到您的每个活动中。它将包含一个TextView
和一个水平放置的Button
。这样做的布局是:
R.layout.actionbar
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_ >
<TextView
android:id="@+id/actionbar_title"
android:layout_
android:layout_ />
<Button
android:id="@+id/actionbar_action"
android:layout_
android:layout_ />
</LinearLayout>
现在假设您的应用中有两个活动,一个是根视图是LinearLayout
(垂直方向),另一个是根视图是RelativeLayout
。上面的布局可以很容易地包含在LinearLayout
中(只需将其放在您想要的位置),RelativeLayout
也可以这样做,当然要考虑到 RelativeLayout
中的当前元素(请记住您必须为include
标记设置layout_width/height
(例如,从包含的布局的根目录复制),以便考虑其他layout_*
属性)。
现在考虑您的建议。布局将变为:
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:id="@+id/actionbar_title"
android:layout_
android:layout_ />
<Button
android:id="@+id/actionbar_action"
android:layout_
android:layout_ />
</merge>
查看我们的ActionBar
布局并不能告诉我们太多,您只需在某处包含TextView
和Button
。现在考虑根是垂直方向LinearLayout
的活动。布局R.layout.actionbar
不能简单地包含,因为这会破坏我们的ActionBar
,我们需要为我们的布局添加一个额外的LinearLayout
(方向为水平)以使其看起来符合需要。如您所见,您处于上述情况(没有merge
标签的布局),但是现在您必须记住将包含的布局包装在具有水平方向的LinearLayout
中,只要父根是LinearLayout
方向垂直。
当根是RelativeLayout
时,情况会变得更糟,您不能简单地将include
标记与merge
一起用于RelativeLayout
(阅读How to get RelativeLayout working with merge and include? 的好问题)。再次,选项是将include
嵌入到LinearLayout
中,这会使您处于没有merge
标记的情况(但现在添加的问题多于解决问题)。阅读此链接的最后一部分http://code.google.com/p/android/issues/detail?id=2863 可能会发现带有include
标签的其他错误。
从我上面的示例中可以看出,默认情况下使用merge
标签可能会在某些情况下导致一些问题。此外,当前系统代表了一种更一致的布局方式(您可以为 include
标签创建布局,就像使用根 View
创建普通布局一样)。此外,merge
标签是一种优化,我认为您不应该尝试优化,直到您开始看到一些性能问题(或者您真的想以复杂性为代价挤压每一次性能下降)。大多数应用程序在当前系统下都可以正常运行,具有相当数量视图的三四级深度布局可以在没有merge
优化的情况下完全没有问题。
merge
标签的另一个问题是,以merge
作为其根的膨胀布局需要在膨胀时将自身附加到父级。如果您要膨胀 R.layout.actionbar
布局,则必须将其附加到父级:
View actionBar = getLayoutInflater().inflate(R.layout.actionbar, root, true);
我不知道这是否是一个真正的限制,也许在极少数情况下它可能会破坏交易。
我对@987654367@ - merge
对使用的看法。
【讨论】:
非常感谢您的深思熟虑的回答。我的建议是include
标记放置在一个 RelativeLayout
中,它插入了一个根为 merge
标记的文件(它的内容是来自我的第一个R.layout.actionbar
) 不起作用。此外,如果您总是添加merge
标签,这意味着LayoutInflater
在某些情况下(例如我的回答中的第一种情况)必须为无用元素(merge
标签)制作一个 xml 文件。跨度>
您在尝试此测试时是否遵循了wrapping the includes in a unique parent container 的建议?尽管即使这确实解决了问题,但我认为这种解决方法的额外ViewGroup
肯定会成为反对以这种方式做事的论据,而不是将ViewGroup
排除在包含的文件之外。对吗?
@JeffAxelrod 起初我没有使用包装器ViewGroup
,重要的部分是无需include
元素的包装器就可以成功(运行后也不起作用第二次测试),因为这将增加另一个额外的ViewGroup
。将深度级别增加 3 只是为了在布局中插入 TextView
和 Button
似乎很多:)以上是关于为啥我们不应该将每个包含的 Android XML 布局包装在一个 <merge> 对中?的主要内容,如果未能解决你的问题,请参考以下文章
我们是不是需要将 xmlns:android="http://schemas.android.com/apk/res/android" 添加到 XML 文件的每个根视图中?
为啥在android应用程序中解析xml时出现nullpointerexception?