第五章 使用基于 XML的布局
从技术上讲,使用纯 Java代码创建 widget并把它连接到 Activity上是可行的,这种方法我们已经在前几章使用过,但是,更通用的方式是使用基于 XML的布局。动态的实例化 widget是为更复杂的情况准备的,在这些复杂的情况下, widget在编译器是不能确定(例如,根据从 Internet上获得的数据显示一组按钮)。
现在使时候介绍 XML布局,并学习如何布局 Android Activity了。
什么是基于 XML 的布局?
顾名思义,基于 XML 的布局就是使用 XML 格式的文件指明 widget 之间的关系, widget 与容器的关系。特别是, Android 认为基于 XML 的布局是一种资源,布局文件存储在你的工程目录下 res/layout 目录内。
每一个 XML 文件包含了特定的布局 widget 和容器的元素树,这些组成了 View 。 XML 元素的属性就是 widget 或容器的属性,这些属性描述了 widget 如何现实或容器的行为。例如,如果一个 Button 元素有一个值为 android:textStyle = "bold" 的属性,就意味着按钮上显示的文字应该以黑体的风格显示。
Android 的 SDK 有一个工具( aapt )使用布局。这个工具会被 Android 工具链(例如, Eclipse , Ant 的 build.xml )自动调用。对于开发人员最重要的是, aapt 会在你的工程里面生成源文件 R.java ,这个会让你通过 Java 代码直接访问布局和 widget ,这会在后面说明。
为什么使用基于 XML 的布局?
几乎所有基于 XML 布局可以做的事情,通过 Java 代码都可以实现,你可以使用 setTypeface() 方法让一个按钮上的文字显示为黑体,而不去使用 XML 布局的属性。因为 XML 布局是另一个你需要留意的文件,所以使用 XML 布局我们需要一个很好的理由。
也许最大的理由是为了更容易创建定义 View 的协助工具,例如,一个 IDE ( Eclipse )中的 GUI 创建器或者一个专门用于 Android GUI 设计的工具,例如 DroidDraw 。原则上,这些 GUI 创建器可以生成 Java 代码,而非 XML 文件。但是对于把这些代码读入一个编辑器,相对于以编程语言存储,如果数据是格式化的如 XML 存储会非常容易。此外,保持生成的代码和手写的代码分离,可以避免在代码重新生成时,生成的代码会破坏手写的代码。 XML 形成了一个中间层,这个中间层既可以被设计工具的人容易的使用,也可以被手写代码的程序员是容易的使用。
用 XML 定义 GUI 也越来越通用。 Microsoft 的 XAML , Adobe 的 Flex 和 Mozilla 的 XUL 和 Android 的方法相同:把布局的细节放在 XML 文件中,把编程的细节放在源文件里(例如: Javascript 对于 XUL )。许多不很著名的 GUI 框架,比如 ZK ,也使用 XML 来定义视图。虽然“随大流”不是最好的策略,但是使用 XML 定义 View 的确可以让其他 XML 定义视图的使用者转移到 Android 更容易一些。
OK , XML 布局文件说明
下面就是上一章示例应用程序 Button的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/button"
android:text=""
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
widget 类的名字 —Button— 就是 XML 元素的名字。因为 Button 是 Android 提供的 widget ,所以你使用类名就可以了。如果你通过创建 android.view.View 创建了自己的 widget ,你就需要使用你创建类的全名引(例如: com.commonsware.android.MyWidget )。
根元素需要生命 Android XML 的命名空间:
xmlns:android=http://schemas.android.com/apk/res/android
其他的元素将会是根元素的子元素,并且会继承名字空间的声明。
因为我们想要从 Java 代码中引用这个按钮,我们需要通过 android:id 属性赋予它一个标识符。我们会在下面详细解释这些概念。
剩下的属性就是这个按钮实例的属性:
· Android:text 表明了按钮现实的初始文字(本例中显示空)
· Android:layout_width 和 android:layout_height 告诉 Android 按钮的宽和高和“ parent ”一样,本例中就是整个屏幕 — 这些属性会在后面章节详细解释
@ 符号是做什么用的
许多 widget 和容器仅仅需要出现在 XML 布局文件中,不需要在 Java 代码中引用。例如,一个静态的标签( TextView )经常仅需要出现在布局文件中表明标签在什么地方出现。这种类型的元素,在 XML 文件中不需要 android:id 属性给他们命名。
你需要在 Java 代码中使用的任何 widget 都需要一个 android:id 。
惯例是使用 @+id/... 作为 id 值, … 表示你的 widget 唯一的名字。在上一节 XML 布局文件例子中, @+id/button 是 Button widget 的标识符。
Android 提供一些特殊的 android:id 值,以 @android:id/... 的形式 -- 我们将会在本书其他章看到。
如果把这些加入到 Java?
假如你已经在名为 main.xml布局文件中给你的 View设置了 widget和容器,你需要所得所有事情就是在 Activity的 onCreate方法中使用布局:
setContentView ( R . layout . main );
这个我们以前使用的 setContentView()一样,传给它一个 View的子类(本例中是一个 Button)。 Android内置的 View,从布局构造,可以从自动生成的代码 R类访问。所有的布局文件都可以在 R.layout下找到,以文具文件的名字为关键字 ---main.xml就是 R.layout.main。
为了访问标识过的 widget,需要使用 findViewById() 方法,传递一个 widget 的数字标识符作为参数。数字标识符由 Android 生成,以 r.id.something 的方式保存在 R 类中( something 就是你需要的 widget )。这些 widget 是 View 的子类,就好像前一章创建的 Button 实例。
其他需要说明的
在以前的 Now示例中,按钮上显示当前的时间,这个时间最后一次点击按钮的时间(或者是 Activity第一次显示的时间,如果没有点击按钮)。
在新修改过的示例中( NowRedux),大部分逻辑相同。但是,本例中不从 onCreate方法中实例化 Button,而是在 XML布局文件中引用 Button。
package com . commonsware . android . layouts ;
import android . app . Activity ;
import android . os . Bundle ;
import android . view . View ;
import android . widget . Button ;
import java . util . Date ;
public class NowRedux extends Activity
implements View . OnClickListener {
Button btn ;
@Override
public void onCreate ( Bundle icicle ) {
super . onCreate ( icicle );
setContentView ( R . layout . main );
btn =( Button ) findViewById ( R . id . button );
btn . setOnClickListener ( this );
updateTime ();
}
public void onClick ( View view ) {
updateTime ();
}
private void updateTime () {
btn . setText ( new Date (). toString ());
}
}
第一点不同,不是从我们创建的 Java 代码中设置内容视图,而是设置 Activity 去引用 XML 布局( setContentView(R.layout.main) )。当我们重建包含引用到我们的布局文件的工程时(存储在工程的 res/layout 目录下 main.xml 文件中), R.java 文件会被更新。
另一点不同时,我们需要在代码中得到 Button 的实例,所以我们使用 findViewById() 方法。 因为我们设置按钮的 id 是 @+id/button ,我们可以用按钮的标识符 R.id.button 来引用它。现在,掌握了 Button 的实例,就可以设置回调函数并设置按我们要求设置按钮的标签了。
运行的结果和以前的 Now 示例相同:
图 4. NowRedux 示例 Activity