[翻译]API Guides - Layouts
官方文档地址:http://developer.android.com/guide/topics/ui/declaring-layout.html
PS:API Guides里面的内容不免都简单些,翻译还是很渣,明明知道意思,但按着它的原话翻感觉就是很怪……
一个布局为用户接口定义了视觉上的结构,像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()很正常啊,这根本就不是反例啊……)
Tip:在Common Layout Objects里学习更多有关于不同的布局类型。在Hello Views教程指南里,也有构建各种各样布局的教程。
编写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文件的语法可以参考Layout Resources文档。
载入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"/>
2.然后创建一个view对象的实例,并从布局中捕获它(通常是在onCreate()方法中):
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其实拥有两对宽度高度值。
第一对称为measured width和measured height。这两个尺寸定义了一个view它期望在它的父级控件中有多大。这两个尺寸可以通过getMeasuredWidth()和getMeasuredHeight()来获取。
第二对就是所谓的width和height,有时候称之为drawing width和drawing height。这两个尺寸都是view在绘制和布局完成之后,在屏幕中的确切尺寸大小。这些值可能会与measured width 和 measured height 不同,但这不是绝对的。这两个值可以通过getWidth()和getHeight()来获取。
为了去测量它的尺寸,一个View要把它的内边距算进去。内边距是一个view的上下左右,四个部分的值来表示的。内边距是用来偏移view里面内容的。比如说,左边距是2将会导致view里面的内容相对于view的左边缘向右偏移2像素,内边距可以通过setPadding(int, int, int, int)来设定,可以通过getPaddingLeft(),getPaddingTop(),getPaddingRight()和getPaddingBottom()来获取。
虽然view可以定义内边距,但它不提供任何对外边距的支持。然而,ViewGroup提供这样的支持。查看ViewGroup和ViewGroup.MarginLayoutParams来获取更多信息。
更多关于尺寸的信息,请参考Dimension Values。
常用的布局(Common Layouts)
每一个ViewGroup的子类都提供了一个唯一的方法去显示你内嵌于它里面的view。下面是一些比较常用的布局类型。
Note:虽然你可以嵌入一个或多个布局在另一个布局里面,以此来构建你的UI,但你应该让你的布局嵌套的少一点。如果你不用嵌套的布局,你的界面会被绘制的更快。(一个宽的视图结构比一个深的视图结构更好。)
Linear Layout
线性布局将它的子元素都单一的组织在水平方向或者垂直方向上。如果窗口的长度超过了屏幕了长度就创建一个滚动条。
Relative Layout
相对布局能让你通过指定彼此之间的相对位置来给子元素定位(A在B的左边)或者与父控件相关(与父控件的顶部对齐)。
Web View
显示web页面。
构建带适配器的布局(Building Layouts with an Adapter)
当你的布局里面的内容是动态的或者不是预先设定的,你可能会需要使用AdapterView的子类的布局对象,它可以在运行时填充新的视图进去。AdapterView类的子类使用一个Adapter来给它的布局绑定数据。Adapter是作用是作为数据源和AdapterView布局的中间人,Adapter获取数据(从数组或者数据库中)然后转换成一个view添加进AdapterView布局中。
常用的布局是:
List View
显示一个可以滚动的单行列表。
Grid View
显示一个可以滚动的网格列表。
给适配器视图填充数据(Filling an adapter view with data)
Android提供一些Adapter的子类,它们非常的有用,可以获取不同类型的数据并为AdapterView构建视图。最常用的是:
比如,如果你有一组字符串想要显示在ListView,初始化一个ArrayAdapter,通过构造方法指定每一个String的布局和String数组:
ArrayAdapter adapter =newArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray);
构造方法里的参数:
然后,简单的在你的ListView上调用setAdapter()方法即可:
ListView listView =(ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
想要自定义每一项的输出,你可以为你的数组中的每一个对象覆写toString()。或者为每一项创建一个view,里面可能需要显示更多的东西,(比如,如果你希望在每一项里显示一个ImageView),继承ArrayAdapter然后覆写getView()方法,去返回一个自己想要的view对象。
当你的数据来自一个Cursor对象的时候,你需要使用这个适配器。当使用SimpleCursorAdapter的时候,你必须指定一个布局供每一行使用。比如,如果你想要创建一个人名和电话的列表,你可以执行一个查询,返回的Cursor包含着每一个人的信息以及电话号码。然后,你需要创建一个字符串数组用来指定Cursor中的每一列都对应着布局中的那个view:
String[] fromColumns ={ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; int[] toViews ={R.id.display_name, R.id.phone_number};
当你实例化SimpleCursorAdapter对象,传递为每一个结果准备布局,Cursor对象包含着结果,所有有两个数组:
SimpleCursorAdapter adapter =newSimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews,0); ListView listView = getListView(); listView.setAdapter(adapter);
SimpleCursorAdapter随后会为每一行都创建一个视图,把fromColums的数据都关联到toViews上面。
如果在你应用的生命内,你改变了适配器所绑定的数据源,你需要调用notifyDataSetChanged()方法。这个会通知它附着的视图数据已经改变了,它应该刷新自己。
处理点击事件(Handling click events)
你可以实现AdapterView.OnItemClickListener接口为每一项设置点击事件。比如:
// 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);