复用Layout
和在C/C++中直接#include相似,Android在XML布局中支持<include>标签。简单的使<include>标签包含另外一个XML布局,如Listing 8-7所示。
<include>标签可以为两个目的使用:
(1) 你期望多次使用同样的布局
(2) 你的布局有个通用部分,依赖于设备配置(比如,屏幕方向:landscapte或者protrait)
Listing 8-7给出了当覆盖某些included的布局的属性的时候,如何多次包含一个布局。
Listing 8-7 多次包含布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include android:id="@+id/myid1"
android:layout="@layout/mylayout"
android:layout_margin="9dip" />
<include android:id="@+id/myid2"
android:layout="@layout/mylayout"
android:layout_margin="9dip" />
<include android:id="@+id/myid3"
android:layout="@layout/mylayout"
android:layout_margin="9dip" />
</LinearLayout>
Listing 8-8给出了如何依赖于设备的方向仅包含布局一次,layout-land/mylayout.xml或者layout-port/mylayout.xml将被包含。(假设这里有两个版本的mylayout.xml,一个在res/layout-land目录,一个在res/layout-port目录)
Listing 8-8 依赖屏幕方向包含Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include android:id="@+id/myid"
android:layout="@layout/mylayout"
/>
</LinearLayout>
当一个布局被包含,可以覆盖一些布局的属性,比如:
(1) root view的id(android:id)
(2) layout参数(android:layout_XXX)
如Listing 8-8所展示的,当布局被inflated的时候,布局的包含是动态的。包含应该在编译的时候完成,否则Android不知道include哪个布局(layout-land/mylayout.xml或者layout-port/mylayout.xml)。这和在C/C++中直接#include不同,C/C++被预处理器在编译时候处理。
View Stubs
就像我们在第一章看到的,懒汉式初始化是延迟初始化、提升性能、潜在的节省内存(当对象从来不会被创建)的方便的技术。
Android为了这个目的定义了ViewStub类。ViewStub是一个轻量级的不可见的View,可以用在你的布局中,允许懒汉式当你需要的时候inflate布局资源。Listing 8-9给出了Listing 8-8的一个修改的版本,给出如何在XML布局中使用ViewStub。
Listing 8-9 ViewStub In XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ViewStub
android:id="@+id/mystubid"
android:inflatedId="@+id/myid"
android:layout="@layout/mylayout"
/>
</LinearLayout>
当线性布局被inflate,它仅包含一个子View:一个ViewStub。被ViewStub指向的布局,@layout/mylayout,可能是一个非常复杂的布局,需要很长的时间去inflate,但是之前没有被inflate。为了inflate这个定义在mylayout.xml中的layout,在你的代码中有两个选择,如Listing 8-10和Listing 8-11所示。
Listing 8-10 在代码中inflating Layout
ViewStub stub = (ViewStub)findViewById(R.id.mystubid);
View inflatedView = stub.inflate(); // inflatedView将是定义在mylayout.xml中的布局
Listing 8-11 在代码使用setVisibility() inflate布局
View view = findViewById(R.id.mystubid);
view.setVisibility(View.VISIBLE); // 使用inflated布局替换view stub
view = findViewById(R.id.myid); // 我们需要获取刚刚得到的inflated view
Inflate布局文件的第一种方式(在Listing 8-10)看起来更加方便,有些人可能会说它有一点问题:代码意识到view是一个stub,需要显式的去inflate它。在大多数情况下,这不是一个问题,它也将是在应用中使用ViewStub的时候推荐的方式。
Inflate布局的第二种方式在Listing 8-11给出,更加的通用的原因是它没有提到ViewStub类。然而,上面的代码仍然意识到它使用一个ViewStub的事实,因为它使用了两个固定id:R.id.mystubid和R.id.myid。为了完全通用,布局文件要定义的像8-12一样,inflate的方式像8-13一样。Listing 8-12和Listing 8-9相同,除了id是被创建的R.id.myid一个,而不是两个。相似的,Listing 8-13和Listing 8-11相同,除了R.id.mystubid被R.id.myid代替。
Listing 8-12 XML中的ViewStub,不覆盖ID
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ViewStub
android:id="@+id/myid"
android:layout="@layout/mylayout"
/>
</LinearLayout>
Listing 8-13 在代码中使用setVisibility() inflate布局
View view = findViewById(R.id.myid);
view.setVisibility(View.VISIBLE); // 使用inflated布局替换view stub
view = findViewById(R.id.myid); // 需要获取刚刚inflated view
Listing 8-14 尽可能调用一次findViewById()
View view = findViewById(R.id.myid);
view.setVisibility(View.VISIBLE); // 使用inflated layout替换view stub(如果在布局中使用stub)
if (view.getParent() == null) {
// 使用了stub,所以我们要使用新inflated view替换它
view = findViewById(R.id.myid);
} else {
// 不做任何事情,我们第一次找到的view是我们期望的
}
在Listing 8-13和Listing 8-14给出的不是通用的,你通常不需要这个方式。通常,你的代码了解布局中使用了ViewStub是可接受的,因此简单的使用8-10的代码就够了。
Layout Tools
为了帮助你创建最好的布局,Android SDK提供了两个简单的工具:hierarchyviewer和layoutopt。你可以在SDK tools目录找到这些工具。
Hierarchy Viewer
Android SDK有一个非常有用的工具查看和分析应用的布局:hierarchyviewer。作为事实,Figure 8-1和Figure 8-2都是使用这个工具产生的。除了给你应用布局的细节,这个工具同样测量它需要多长时间去测量、布局、draw每个widget,识别出哪个widget需要比较长的时间去测量、布局和draw。
你可以使用hierarchyviewer作为一个单独的工具,或者直接在Eclipse中使用Hierarchy View视图。
layoutopt(在SDK 16以后使用lint)
Android SDK有另外一个工具可以帮助你分析你的布局:layoutopt。这个工具分析你的布局文件,给出使布局更加有效的改变建议。
比如,对Listing 8-5布局使用layoutopt工具,得到如下的输出:
The root-level <FrameLayout /> can be replaced with <merge/>
事实证明,这是Listing 8-6做的。对Listing 8-6使用layoutopt不会有任何的提醒。
TIP:使用最新的layoutopt工具,以保证你得到最好的结果。
保证layoutopt工具报的所有的问题在发布应用前有认真的对待。不优化布局会使应用变慢,而补救这样的布局通常很简单。