一个布局为用户接口定义了视觉上的结构,像
activity
或
app widget
的UI界面。你可以使用两种方式来声明布局:
- 在XML中定义UI元素。Android提供了一个直截了当的XML词汇表,与那些小控件,布局的视图类以及它们的子类关联。
- 在运行时实例化布局元素。你的应用可以通过编程的方式创建View和ViewGroup对象(并且操纵它们的属性)。
Android框架给你提供了极大的灵活性,你可以使用两种方式让你来声明,管理你的UI。比如说,你可以在XML中声明你应用的默认布局,包括要显示在屏幕上的元素和它们的属性。随后你可以在运行时的代码中修改它们的状态,包括那些已经在XML中声明的对象。
在XML中声明你的UI的好处是你可以更好的将应用的声明与应用的功能分开。你的UI的描述对于程序代码来说是额外的,这意味着,你可以不通过修改源代码或者重编译来修改或者适配它们。比如说,你可以为不同屏幕方向来创建XML布局,不同的设备屏幕尺寸,不同的语言。另外在XML中声明这些布局更容易让你看清UI的结构,这样就很容易发现bug。所以这篇文档着眼于教你如何在XML文件中声明布局。如果你对运行时实例化View对象比较有兴趣的话,请参考
ViewGroup和
View的参考。
通常情况下,用来声明UI元素的XML词汇都遵循着类的命名规则与方法,这也包括那些类中的属性名。事实上,因为这样的关联,你可以直接猜到XML属性对应着类里面的哪一个方法,或者哪个类对应着哪一个XML元素。然而,你需要记住,不是所有的词汇都是对应的。在一些情况下,有一些少量的不同的命名。比如,EditText元素有text属性,这个属性关联着EditText.setText()。(译者注:text关联setText()很正常啊,这根本就不是反例啊……)
编写XML文件(Write the XML)
通过使用Android的XML词汇,你可以快速的设计UI布局和屏幕中所包含的元素,如同通过HTML创建网页一——通过一系列内置的元素。
每个布局文件必须包含一个正确的根元素,它必须是一个View或者ViewGroup对象。当你定义好根元素之后,你可以添加额外的布局对象或者小控件作为他们的子元素,以此来构建View的层级结构。比如,下面的XML布局使用了一个垂直的LinearLayout包含了一个TextView和一个Button控件。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextViewandroid:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a TextView"/>
<Buttonandroid:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a Button"/>
</LinearLayout>
当你在XML中声明完你的布局,并以.xml后缀保存在Android项目的res/layout/目录下之后,它就会被正确的编译。
载入XML资源(Load the XML Resource)
当你编译你的应用程序的时候,每一个XML文件会被编译成一个View资源。你应该通过你的程序代码载入布局文件资源,在你的Activity.onCreate()回调里实现。只要调用
setContentView()就可以了,传递一个如下格式的布局资源的参数:R.layout.layout_file_name。如果你的XML布局文件保存为main_layout.xml,你应该在Activity中载入它,像这样:
publicvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
}
onCreate()回调方法是在Activity启动的时候由Android框架负责调用的(关于生命周期的讨论,请查看
Activities文档)。
属性(Attributes)
每个View和ViewGroup对象支持众多的XML属性。一些属性是特定于一个View对象的(比如,TextView有textSize属性),但很多属性都是继承于它们的父类View。所以所有的View对象都是相似的,因为它们都继承于View类(比如所有的view都有id属性)。其它的一些属性像“layout parameters”,这些属性是用于描述View对象里面的具体的布局细节,方向灯,这些都是在父类ViewGroup中定义的。
ID
任何一个View对象都有一个整型的ID与之关联,使之在树中有唯一的标示符。当应用程序被编译的时候,ID将作为一个整型,但在XML布局文件中,ID属性通常都是一个字符串。这对大部分View对象都是通用的,你会经常使用到它,在XML标签里面,ID的语法如下:
android:id="@+id/my_button"
在字符串开头的at符号(@)指明XML解析器应该解析ID字符串剩下的部分并标示它为一个ID资源。加号(+)表示这是一个新的资源名,应该被创建并且添加到资源中去(在R.java文件中)。Android框架提供了一些ID资源。当你要引用Android资源ID,你不需要加上加号,但你必须要加上android包的命名空间,像这样:
android:id="@android:id/empty"
当我们使用android包命名空间的时候,我们正在使用一个来自android.R资源类的ID,而不是本地的资源类。
为了创建Views,并且能在程序里引用它们,通常是这样的模式:
1.在布局文件中定义一个view/widget,并注册一个唯一的ID:
<Buttonandroid:id="@+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/my_button_text"/>
Button myButton =(Button) findViewById(R.id.my_button);
当使用
RelativeLayout的时候,为view对象定义ID是非常重要的。在相对布局中,view对象定义它们的布局关系通过其它的view对象,这都必须引用唯一的ID。
在整个树中,ID不需要是唯一的,但是,你应该要确保在你搜寻的局部的树中,ID必须是唯一的。(通常都是搜寻整个树,所以最好的办法就是ID绝对唯一。)
布局参数(Layout Parameters)
XML布局属性都是叫layout_something的,这些属性定义了View在ViewGroup中的正确位置。
每个ViewGroup类都实现了内置的一个类
ViewGroup.LayoutParams。这个子类包含了各种属性来定义子元素的大小和位置,使它们可以在正确的位置。如figure 1所示,父类view group为每一个子view都定义了布局参数(包括子view group)
记住没个LayoutParams子类都有自己的语法去设置值。每一个子元素必须定义LayoutParams用来适应它的父类,虽然这可能会为它自己的子元素定义不同的LayoutParams。
所有的view group都包含了宽度和高度(layout_width和layout_height),并且每一个view都必须定义这两个属性。大部分LayoutParams也有可选的外边距和边界。
你可以使用精确的值来指定宽度和高度,虽然你不会经常这么做的。通常你会使用下面的常量来设置宽度和高度:
- wrap_content 告诉你的view,它的尺寸必须要包住里面的内容。
- fill_parent(在API LEVEL 8 改名为match_parent) 告诉view必须要和父ViewGroup有一样的大小。
通常,使用绝对精确的单位,比如像素,来指定宽度和高度是不推荐的。取而代之的,我们使用与尺寸相关的单位,像像素无关单位 dp,wrap_content或者fill_parent是更好的选择,因为它能确保你的应用可以适应更多的设备的屏幕尺寸。允许的尺寸类型在
Available Resources文档中定义。
布局位置(Layout Position)
View在几何学上都是矩形的。一个View有它的位置,通过左和上的坐标来确定,有两个尺寸,通过宽度和高度来指定。位置和尺寸的单位都是像素。
是可以通过调用View的getLeft()和getTop()方法来获取到View的位置的。前一个方法返回左,或者称为表示view的矩形的X坐标。后一个方法返回顶,或者view的矩形的Y坐标。这两个方法返回值都是与view的父级视图相关的,比如说:当getLeft()返回20,者意味着view位于它直接父级控件的左边缘偏右20像素的地方。
另外,一些方便的方法也提供给你了,避免无谓的计算,它们是getRight()和getBottom()。这两个方法返回view矩形的右边的坐标和底部的坐标。比如:调用getRight()就相当于如下的计算:getLeft() + getWidth()。
大小,内边距与外边距(Size, Padding and Margins)
一个view的大小是通过宽度和高度来指定的。一个view其实拥有两对宽度高度值。
第二对就是所谓的width和height,有时候称之为drawing width和drawing height。这两个尺寸都是view在绘制和布局完成之后,在屏幕中的确切尺寸大小。这些值可能会与measured width 和 measured height 不同,但这不是绝对的。这两个值可以通过
getWidth()和
getHeight()来获取。
常用的布局(Common Layouts)
每一个
ViewGroup的子类都提供了一个唯一的方法去显示你内嵌于它里面的view。下面是一些比较常用的布局类型。
Note:虽然你可以嵌入一个或多个布局在另一个布局里面,以此来构建你的UI,但你应该让你的布局嵌套的少一点。如果你不用嵌套的布局,你的界面会被绘制的更快。(一个宽的视图结构比一个深的视图结构更好。)
Linear Layout
线性布局将它的子元素都单一的组织在水平方向或者垂直方向上。如果窗口的长度超过了屏幕了长度就创建一个滚动条。
Relative Layout
相对布局能让你通过指定彼此之间的相对位置来给子元素定位(A在B的左边)或者与父控件相关(与父控件的顶部对齐)。
Web View
显示web页面。
构建带适配器的布局(Building Layouts with an Adapter)
常用的布局是:
List View
显示一个可以滚动的单行列表。
Grid View
显示一个可以滚动的网格列表。
给适配器视图填充数据(Filling an adapter view with data)
ArrayAdapter adapter =newArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, myStringArray);
构造方法里的参数:
ListView listView =(ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
String[] fromColumns ={ContactsContract.Data.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER};
int[] toViews ={R.id.display_name, R.id.phone_number};
SimpleCursorAdapter adapter =newSimpleCursorAdapter(this,
R.layout.person_name_and_number, cursor, fromColumns, toViews,0);
ListView listView = getListView();
listView.setAdapter(adapter);
处理点击事件(Handling click events)
// Create a message handling object as an anonymous class.
privateOnItemClickListener mMessageClickedHandler =newOnItemClickListener(){
publicvoid onItemClick(AdapterView parent,View v,int position,long id){
// Do something in response to the click
}
};
listView.setOnItemClickListener(mMessageClickedHandler);