<mearge/>
标签是为了减少视图树中的视图层级优化Android布局而创建的。为了更容易理解这个标签是如果解决问题的,我们看一个例子。下面的XML布局声明了一个图片和图片上有个标题的布局,结构相当简单;一个FramLayout内堆叠一个ImageView和上面的一个TextView:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center"
android:src="@drawable/golden_gate" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dip"
android:layout_gravity="center_horizontal|bottom"
android:padding="12dip"
android:background="#AA000000"
android:textColor="#ffffffff"
android:text="Golden Gate" />
</FrameLayout>
这个layout渲染很好的达到了我们的预期,似乎没有什么问题:
当你使用HierarchyViewer检查后,事情变得更加有趣,你会发现,在我们的XML文件中定义的FrameLayout(下面蓝色高亮显示)是一个FrameLayout唯一的孩子:
由于我们的FrameLayout具有和它父控件相同的尺寸,通过使用fill_parent限制,并没有定义任何背景,额外的填充或权重,这完全没必要。我们只是实现UI没有理由让它更复杂。但是我们如何摆脱这种FrameLayout呢?毕竟,XML文档需要一个根标签而一个标签在XML中意味着一个视图实例。
这时< mergre/>标签就派上用场了。当LayoutInflater遇到这种标签,它会跳过它并将< merge/>的孩子放到< merge/>父控件中。让我们修改之前XML文件,用< merge/>替换FrameLayout:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center"
android:src="@drawable/golden_gate" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dip"
android:layout_gravity="center_horizontal|bottom"
android:padding="12dip"
android:background="#AA000000"
android:textColor="#ffffffff"
android:text="Golden Gate" />
</merge>
修改后的布局,TextView和ImageView都将直接添加到顶层的FrameLayout中。其视觉结果是一样的,但视图层次比较简单:
显然,使用< merge/>的这种情况下是因为Activity内容视图的父控件总是一个FrameLayout。如果你的布局使用LinearLayout作为它的根标签并不适用。< merge/>在其他情况下非常有用。例如,可以和< include/>标签完美结合。你可以使用< merge/>创建一个定制的复合视图。让我们看看如何使用这个标签创建一个名为OkCancelBar的视图,它只是显示了自定义标签的两个按钮。这是在图片上面使用XML显示的自定义视图:
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center"
android:src="@drawable/golden_gate" />
<com.example.android.merge.OkCancelBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingTop="8dip"
android:gravity="center_horizontal"
android:background="#AA000000"
okCancelBar:okLabel="Save"
okCancelBar:cancelLabel="Don't save" />
</merge>
这个布局在设备上的显示效果如下:
OkCancelBar的源码非常简单,在外部的XML中只定义了两个按钮,并使用LayouInflate加载。正如下面的代码片段所示,R.layout.occancelbar XML布局文件是作为OkCancelBar控件的父控件被装载的。
public class OkCancelBar extends LinearLayout {
public OkCancelBar(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(HORIZONTAL);
setGravity(Gravity.CENTER);
setWeightSum(1.0f);
LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0);
String text = array.getString(R.styleable.OkCancelBar_okLabel);
if (text == null) text = "Ok";
((Button) findViewById(R.id.okcancelbar_ok)).setText(text);
text = array.getString(R.styleable.OkCancelBar_cancelLabel);
if (text == null) text = "Cancel";
((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);
array.recycle();
}
}
这两个按钮都是在下面的XML布局中被定义。正如你看到的,我们使用< merge/>标签来将两个按钮直接添加到OkCanelBar中,每个按钮都来自同一个外部XML布局文件使他们更易于维护,我们只是重写其ID:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<include
layout="@layout/okcancelbar_button"
android:id="@+id/okcancelbar_ok" />
<include
layout="@layout/okcancelbar_button"
android:id="@+id/okcancelbar_cancel" />
</merge>
我们创建了一个灵活且易于维护能产生有效视图层级的自定义视图:
< merge/>标签非常有用,使代码更加神奇。然而,它使用受到一下限制:
- 在XML布局中< merge/>只能作为根标签。
- 当一个布局由< merge/>开始填充,你必须指定一个父ViewGroup并且必须设置attachToRoot为true()见文档中的inflater()方法)。