【Android Developers Training】 3. 构建一个简单UI
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。
原文链接:http://developer.android.com/training/basics/firstapp/building-ui.html
一. 构建一个简单UI
一个Android应用通过View和
ViewGroup对象所组成的层次结构来构建图形用户接口(GUI)。通常View对象是一些UI控件,比如按钮(buttons),文本框(text fields)等。ViewGroup对象则是一些可见的装载View对象的“容器”,同时它定义了它的子View对象是如何布局的,比如,定义子对象是应该在网格状布局列表中还是再垂直布局的列表中。
Android为每一种View和ViewGroup的子类提供了在XML中所对应的词汇,所以用户可以在XML中通过使用一个UI元素的层次关系来定义自己的UI。
图1. ViewGroup的分支布局结构,每个ViewGroup下包含了其他的View对象。
在这堂课中,你将会通过XML语句创建一个布局,这当中包含了一个文本框和一个按钮。在后续课程中,你将会实现按钮按下后的响应操作:把文本框中的内容发送给另一个Activity。
实现布局方式的选择:在XML中声明UI布局而不是在运行时通过代码来声明是更加有效的,这其中有一些原因,最重要的,是你可以为不同规格的屏幕创建不同的布局。比如,你可以创建两个版本的布局文件,并且告诉一同在小屏幕设备上使用其中一种布局,在大屏幕设备上使用另一种。可以通过阅读Supporting Different Devices获取更多这方面的信息。
一). 创建一个线性布局
在“res/layout/”这一目录下,打开文件:“activity_main.xml”
注:在Eclipse中,当你打开一个布局文件,你首先看到的将会是图形布局编辑器(Graphical Layout editor),通过使用WYSIWYG工具(译者注:WYSIWYG意为所见即所得),这个工具可以帮助你构建布局。在这一堂课当中,你将直接通过XML语句实现布局,所以点击屏幕底部activity_main.xml这一标签进入XML编辑器。
当你创建这个项目工程的时候,你选择的BlankActivity模板包含了这个activity_main.xml布局文件,在该文件当中,有一个RelativeLayout的根View和一个TextView的子View。
首先,删除 <TextView>这一标签,同时将
<RelativeLayout>改为
<LinearLayout>,并且加上
android:orientation这一属性字段,将它设置为:“horizontal”。修改后的结果如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > </LinearLayout>
这里LinearLayout是一个View Group对象(ViewGroup的一个子类),从方向上来说,它将它的子View布局在水平方向或垂直方向上,具体的方向由android:orientation这一属性字段定义。每一个LinearLayout的子View在屏幕上出现的先手顺序与它在XML代码里出现的先后顺序一致。
另外两个属性字段:android:layout_width和
android:layout_height对所有View对象来说都是必须的,因为它们定义了View的尺寸。
由于LinearLayout是布局的根View,所以应该通过把它的宽和高设置为"match_parent",以此使它填满整个屏幕区域。上述提到的这一属性值的含义即将该View的宽和高延展只和它的父View的宽高一致。
更多信息,可以阅读Layout帮助文档。
二). 添加一个文本框
为了创建一个用户可编辑的文本框,添加一个<EditText>标签在<LinearLayout>中。
如同每一个View对象,你必须通过定义特定的XML属性字段来定义
EditText对象的属性。这里展示你应该在
<LinearLayout>标签中如何声明它:
<EditText android:id="@+id/edit_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="@string/edit_message" />
关于这些属性字段:
为该View提供唯一的Id标识,在你的应用代码中,你可以通过它来引用该对象,进而读取和操作这一对象(在下一次课程中将会学习的内容)。
这个at(@)的符号,是当你在XML中引用任意资源对象所必须的。紧随它之后的是资源的类型(在此例中,类型是id),一个斜线,和资源对象的名字(edit_message)
这个在资源类型前的加(+)号,仅在你第一次定义一个资源ID时需要。当你编译你的应用时,SDK工具使用这个ID名字在你项目中的“gen/R.java”这一文件下会创建一个新的资源ID,该新ID会代表我们的EditText元素。一旦这个资源ID通过这种方式声明好后,在其他地方对这个ID的引用就不在需要加号了。记住:仅在声明一个新的资源ID的时候需要使用加号,对于一些固有的资源,比如字符串(string),布局(layout)来说则不需要。
关于资源对象:
简单的来说,一个资源对象就是与一个应用资源(比如一个位图,一个布局文件或一个字符串)唯一关联起来的整形形式的名字。在你的项目工程的gen/R.java文件下,每一个资源有一个与之对应的资源对象。你可以通过使用在R类中的对象名称来引用它对应的资源,比如:当你需要为
android:hint这一属性字段定义一个字符串的值时。同时你也可以通过在
android:id这一属性字段,
创建一个与该View关联的资源ID,以此使你可以在其他的代码位置对这个View进行引用。当你每次编译你的应用时,SDK工具会自动生成R.java文件,你应该永远都不要手动修改这一文件。
阅读Providing Resources来获取更多信息。
android:layout_width
和android:layout_height
除了为宽和高定义特定的尺寸,使用"wrap_content"这一属性值意味着该View的大小刚好能适应于它的内容。如果你使用"match_parent",这个EditText控件将会充满整个屏幕,因为它将会和它的父View(LinearLayout)的尺寸保持一致。阅读Layouts获得更多信息。
这是一个当文本框内容为空时,默认显示的字符串。除了直接定义字符串的值外,还可以通过"@string/edit_message"
这样的方法来引用一个在其他文件定义了的字符串资源。因为这个引用是指向一个固有的资源(不是一个ID标识),所以不需要加上加号。然后,因为你还没有定义这个字符串资源,所以你将会看到一个编译错误,你将在下一节通过定义这个字符串来解决这个问题。
注:这个字符串资源和这个空间的ID有着相同的名字:
edit_message,然而,对资源的引用会被限制在不同资源类型特定的作用域内,因此在此例中,使用相同的名字也不会产生命名冲突。
三). 添加字符串资源
当你需要在UI中添加文本时,你应该经常讲字符串定义为资源的形式,字符串资源可以允许你在同一个地方统一管理所有的UI文字,使得寻找和更新文字变得轻松。同时,将字符串放在外部可以允许你通过为每一个字符资源提供不同的定义,解决你的应用对于不同语言的本地化问题。
默认的,你的Android项目工程包含了一个字符串资源文件,这个文件位于:“res/values/strings.xml”。现在我们添加一个新的字符串,将它命名为“edit_message”,并且把它的值定义为“Enter a message”(你可以将字符串“Hello World”删除)。
与此同时,添加一个值为“Send”的字符串,它的名字为“button_send”,这是为之后将要添加的按钮控件做的准备工作。
修改后的strings.xml变为:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">My First App</string> <string name="edit_message">Enter a message</string> <string name="button_send">Send</string> <string name="action_settings">Settings</string> <string name="title_activity_main">MainActivity</string> </resources>
关于使用字符串资源解决应用对于不同语言的本地化问题,更多的信息可以阅读:Supporting Different Devices
四). 添加一个按钮
现在在布局中添加一个<Button>标签,它紧接着<EditText>标签:
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button_send" />
把高度和宽度设置为“wrap_content”,这样按钮的大小正好适应于按钮的文字。这个按钮控件暂时不需要android:id这一属性字段,因为它在activity的代码中暂时不会被引用到。
五). 使输入框的宽度与屏幕宽度对应
现在的布局被设计成EditText和
Button这两个控件的大小和它们的内容相适应,如图2所示:
图2. 宽度设置为“wrap_content”后的EditText
和Button
这样做对于按钮来说没有什么问题,但是对文本框来说可能就不太好,因为用户可能会输入更长的字符串。所以最好是屏幕中没有用到的宽度给文本框使用。我们可以在LinearLayout中使用比重(Weight)属性来实现,具体来说是使用
android:layout_weight这一属性字段。
比重的值是一个数字,这个数字用来表征每个View相对于它兄弟View所消耗的空间来说,可以使用的剩余的空间大小。可以理解为饮料的配方:“2份伏特加和1份咖啡甜酒”,这就意味着饮料中的三分之二是伏特加。例如:如果你给了一个View的比重是2,而另一个View的比重是1,那么他们的和是3,所以第一个View消耗剩余空间的三分之二,而第二个View消耗三分之一。如果你又添加了第三个View,它的比重是1,那么现在第一个View将会获得剩余空间的一半,另外两个则是四分之一。
默认所有的View的比重是0,所以如果你仅对一个View的比重定义为大于0的任何数,那么这个View将会拿到剩余的所有空间,而其他View则仅拥有他们所需要的空间。所以,为了让EditText把剩余空间填满,把它的比重设置为1,同时让按钮没有比重就可以实现了。
<EditText android:layout_weight="1" ... />
当你定义了比重时,为了提高布局的效率,我们把EditText的宽度设置为0(0dp)。将宽度设置为0有利于提高布局性能,因为使用"wrap_content"作为宽度的话需要系统计算控件的宽度,而这是不必要的,因为这个宽度值需要另一个控件的宽度计算结果,以此来进一步的填满屏幕的宽度。
<EditText android:layout_weight="1" android:layout_width="0dp" ... />
图3展示了把比重全部分配给EditText后的运行结果:
图3. EditText拥有了布局的所有比重,所以它填满了LinearLayout剩余的空间
现在,修改后的布局文件应该是这样的:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <EditText android:id="@+id/edit_message" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="@string/edit_message" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button_send" /> </LinearLayout>
这个布局将会被当你创建项目工程时,SDK工具生成的默认Activity类所应用,现在你可以运行这个应用来看看结果:
- 在Eclipse中,点击工具栏中的“运行”。
- 或者在命令行下,将工作目录切换至该Android项目的根目录,然后执行:
ant debug
adb install bin/MyFirstApp-debug.apk
下一节课程将会进一步学习按钮的响应,从文本框中读取数据和启动一个新的activity等知识。