Android开发指南中文版(九)User Interface-XML Layouts
定义布局
布局指的是Activity中的UI的结构。它定义了布局结构并持有所有用户能看到的元素。你可以有两种方式来定义你的布局:
- 在xml中定义UI元素。Android提供了一整套和各种View类对应的易懂的xml语法,例如各种widget和layout等。
- 在运行时初始化Layout元素。你的应用程序可以使用代码创建View和ViewGroup对象。
Android框架通过这两种方法并存的方式提供了灵活的UI定义方式。例如,你可以在xml里定义你的应用程序的默认布局,并在运行时中修改屏幕上的对象。
在xml中定义UI的好处是它让你更好的将应用程序的外观和控制行为的代码分开。你的UI描述在应用程序的代码之外,这意味着你可以在不修改代码和重新编译的前提下修改UI。例如,你可以为不同的屏幕朝向,不同的屏幕大小和不同的语言设定不同的布局。另外,在xml中定义布局使得你的UI更加直观和容易调试(xml的结构比java代码更清晰)。本文介绍如何在xml中定义布局。
总的来说,定义UI的xml词汇和相应的类名、方法名是紧密相关的;xml的元素名对应类名,而xml的属性名对应方法名。事实上,它们之间的关系通常非常直接以至于你可以从xml属性名猜出方法名,或者从xml元素猜出java类名。然而,注意不是所有的词汇都是相同的。在某情况下二者的命名有些小的区别。例如,EditText元素有一个text属性对应于一个叫做EditText.setText()的属性。
- Eclipse的ADT插件给你提供了xml中的布局预览——打开xml文件,并选择layout标签。
- 你也可以尝试一下层次浏览器工具(在sdk/tools下),用来调试布局——它显示了布局属性值,根据画出线图来表示padding/margin以及渲染后的view的外观。
- layoutopt工具可以让你快速分析你的布局和层次是否有效率和其它的问题。
Write the XML
使用Android的xml词汇,你可以很快设计UI布局和它包含的各种屏幕元素,就像使用HTML来创建网页一样——使用一系列嵌套的元素。
每个布局文件必须包含一个根元素,该元素必须是一个View或者ViewGroup对象。一旦你定义了根元素,你就可以加入额外的布局对象或者widget作为其子元素,逐渐构成你的完整布局。例如,这里有一个使用从上到下的LinearLayout布局,它包含了一个TextView和一个Button:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, I am a TextView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, I am a Button" /> </LinearLayout>
使用xml定义了布局之后,将它保存在Android工程的res/layout目录中才能正确编译。
Load the XML Resource
当你编译你的工程时,每个xml布局文件被编译成一个View资源。你应该在应用程序代码中加载这些布局资源,在Activity.onCreate()回调函数的实现中。调用setContentView(),并将布局资源的引用R.layout.layout_file_name传给它。例如,如果你的布局xml文件名为main_layout.xml:
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView.(R.layout.main_layout); }
onCreate()在你的Activity启动时被调用。
Attributes
每个View和ViewGroup对象都支持它们各自的xml属性。有些属性只对某一种View对象有效(例如,TextView支持textSize属性),这些属性对继承这些view的对象也有效。有些属性对View对象普遍有效,因为它们继承于View基类(例如Id属性)。还有的属性被认为是“布局参数”,用来描述View对象的布局属性。
ID
任何View对象可能会有一个整数ID和它相关联,在一棵树中唯一的指定一个View对象。当应用程序被编译时,该id被当做一个整数处理,但是该ID一般是在布局xml里作为一个字符串出现,作为id属性的值。这是一个所有View对象都有的xml属性(由View基类定义)。在xml标签中定义一个id的语法如下:
android:id="@+id/my_button"
字符串开始的@记号告诉xml解析器应该将后面的部分展开并将其识别为一个ID资源。+表示这是一个新的资源名称,必须将其加入我们的资源集(R.java)。有一些其它的ID资源是由Android框架提供的,当引用它们时,不需要使用+符号,但必须加上Android包名称空间,像这样:
android:id="@android:id/empty"
有了Android包名称空间,我们现在指向的是android.R资源类的对象,而不是本地资源类中的对象。
为了创建view并从应用程序中引用它们,下面是一个基本的模式:
- 定义一个view/widget并给它分配一个唯一的ID:
<Button android:id="@+id/my_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/my_button_text"/>
- 然后创建一个该View对象并从布局中得到它(一般在onCreate()中):
Button myButton = (Button) findViewById(R.id.my_button);
为View对象定义id,在创建RelativeLayout(相对布局)时是很重要的。在一个相对布局中,兄弟View(位于树结构的同一层的两节点)可以定义它们相互之间的的相对位置,这需要id来标识View对象。
一个ID不需要在整个树中唯一,但它必须在你正在搜索的树的部分中唯一(常常就是整个树,因此最好尽量使用唯一的id)。
Layout Parameters
名称为layout_something的xml布局属性定义了一个View在它所在的ViewGroup中的布局参数。
每个ViewGroup类实现一个嵌套类,该类是ViewGroup.LayoutParams的子类。该类包含了定义每个子View大小和位置的属性。如下图所示,父view group定义了子view的布局参数:
注意每个LayoutParams子类有着它自己的设置值的方法。每个子元素必须定义和其父元素相应的LayoutParams,尽管它可以对它自己的子元素定义不同的LayoutParams。
每个view group包含一个宽度和高度(layout_width和layout_height),每个view必须定义它们。很多LayoutParams也包括可选的margin(空白)和border(边界)。
你可以使用精确值来指定宽度和高度,尽管你可能很少这样使用。更常见的是,wrap_content和fill_parent:
- wrap_content把view对象的大小设为和它的内容相合适。
- fill_parent (renamed match_parent in API Level 8) 尽可能的大将其父对象填满。
Layout Position
view的几何性质就是它所在矩形的几何性质。一个view的位置用一对left和top坐标表示,两个长度用width和height来表示。位置和长度的单位是像素。
通过调用getLeft()和getTop()可以得到一个View的位置。这两个方法返回的是相对于其父元素的位置,例如,如果getLeft()返回20,则表示它和其直接父元素的左边界的距离是20像素。
另外,有一些方法的存在只是为了写程序的方便,如getRight()和getBottom()。getRight()和getLeft()+getWidth()的作用是一样的。
Size, Padding and Margins
一个view的大小使用宽度和高度来描述。一个view实际上有两组高度和宽度值。
第一组成为测量宽度和测量高度。这些长度定义了一个view希望在它的父view中的大小。测量长度可以使用getMesuredWidth()和 getMeasuredHeight()来得到。
第二组为宽度和高度,有时被成为绘制宽度和绘制高度。这些长度定义了view在屏幕上的实际大小。这些值可能和测量长度不同。它们可以通过getWidth()和getHeight()得到。
为了测量它的长度,一个view将它的padding考虑在内。padding使用像素来表示,可以设定上下左右各个方向上的padding。padding可以用来将view产生一个位移。例如,一个2像素的左padding将使view向右偏移2个像素。可以使用setPadding(int,int,int,int)来设定,由getPaddingLeft(), getPaddingTop(), getPaddingRight() 和 getPaddingBottom()来获取。
虽然一个view可以定义一个padding,但它并不支持margin。view group支持margin。