Android的布局技巧 #3:通过合并优化

参考Android Layout Tricks #3: Optimize by merging

<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()方法)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值