佈局
佈局定義了用戶界面的可視話結構,比如 activity 或 widget 的UI。你可以用下面兩種方式聲明佈局:
- 在XML中聲明UI元素. Android提供了與視圖類和子類名一致的一些簡單的XML詞彙,比如那些窗口部件和佈局。
- 運行時初始化佈局元素. 你的應用可以以編程的方式創建View和ViewGroup(並且可以修改它們的屬性)。
Android框架允許你靈活的獨自使用或全部使用這些方法去聲明和管理應用的UI。例如,你可以在XML中聲明應用的默認佈局,包括會在佈局中顯示的屏幕元素和它們的屬性。然後在運行時你可以在應用裏添加代碼來修改屏幕對象的狀態,包括那些在XML聲明的對象。
- ADT Plugin for Eclipse 可以在佈局的XML文件打開並選中Layout標籤時爲你提供佈局的預覽。
- 你也應該嘗試使用 Hierarchy Viewer 工具來調試佈局,它可以獲取佈局屬性值,使用指示器繪製出padding/margin的線框,完整的呈現你在模擬器或設備上調試時的視圖。
- layoutopt工具可以讓你快速分析佈局和層次的來解決低效和其他問題。
在XML裏聲明UI的優勢是它使應用的外觀從控制它行爲的代碼中分離出來成爲可能。用戶界面的描述與應用的代碼是不相關的,這意味着你可以在不修改源代碼和重新編譯的情況下修改或適配用戶界面。例如,你可以根據不同的屏幕方向、不同的屏幕尺寸和不同的語言設置XML佈局。此外,在XML中聲明佈局使用戶界面可視化更容易,所以調試問題會更簡單。照此,這篇文檔集中於如何在XML中聲明佈局的教學。如果你對於在運行時實例化視圖對象感興趣,請參考 ViewGroup
和 View
類的參考文檔。
一般來說,聲明UI元素的XML詞彙效仿類和方法的結構和命名,元素名與類名保持一致,屬性名與方法名保持一致。實際上,你可以直接依據類的方法來猜測XML的屬性,依據XML元素來猜測類名。然而,注意並不是所有的詞彙都這樣。有時候,它們的命名稍有不同。例如,EditText元素的 text
屬性對應於 EditText.setText()
。
小貼士:在 Common Layout Objects 中學習更多關於不同類型佈局的知識。在 Hello Views 教學指南中有構建各種佈局的教學。
編寫XML
學會Android的XML詞彙後,你可以很快的用一系列的內聯元素設計出UI佈局和包含在其中的屏幕元素,就像你使用HTML創建網頁一樣。
每個佈局文件必須恰好包含一個根元素,它必須是一個View或ViewGroup對象。一旦你定義了根元素,你可以添加額外的佈局對象或窗口部件作爲子元素來逐漸的構建那個定義你佈局的視圖層次。例如,下面是使用垂直的 LinearLayout 來包含一個 TextView 和一個 Button 的XML佈局:
<?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中聲明好你佈局後,使用.xml擴展名把這個文件保存到你的Android工程裏的res/layout/
目錄中,這樣它纔會正確的編譯。
更多關於佈局的XML文件中語法的信息可以從 Layout Resources 文檔中獲得。
加載XML資源
編譯應用時,每個XML佈局文件會被編譯進一個 View 資源中。你應該在應用的 Activity.onCreate() 回調實現的代碼里加載佈局資源。可以通過調用 setContentView()
來加載佈局資源,需要把佈局資源的引用以R.layout.layout_file_name格式傳遞給這個方法。例如,如果你的XML佈局保存爲main_layout.xml
,你應該像這樣爲你的Activity加載它:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
}
Activity裏的 onCreate()
回調方法會在Activity啓動時被Android框架調用(參考 Activities 文檔中關於生命週期的討論)。
屬性
每種View和ViewGroup對象都支持它們自己的多種XML元素。有些屬性是視圖對象獨有的(例如,TextView支持的textSize
屬性),但是這些屬性也可以被任何繼承該類的視圖對象繼承。有些屬性是所有視圖對象共有的,因爲它們繼承自跟視圖類(比如id屬性)。另外,其他用來確定視圖對象佈局方向的被稱爲“佈局參數”的屬性是由對象的父ViewGroup對象定義。
ID
任何視圖對象都可以有一個與之關聯的用來在樹中唯一標識視圖的整型ID。當應用被編譯後,這些ID會被引用爲整型,但是在佈局XML文件中的id屬性要指定爲字符串類型。這是所有視圖(View 類定義的)對象共有的XML屬性,你以後會經常用到的。在XML標籤裏ID的語法是這樣的:
android:id="@+id/my_button"
在字符串前面的at符號(@)表明XML解析器應該把剩餘的ID字符串當做ID資源來解析和擴展。加號(+)表示這是一個新的資源名, 它必須被創建和添加到我們的資源中(在R.java文件中)。剩下的其他ID資源由Android框架提供。要引用一個Android資源ID時,你不需要使用加號符,但是必須添加android
包命名空間,就像這樣:
android:id="@android:id/empty"
在適當的地方使用android
包命名空間,我們現在可以從android.R資源類中引用ID,而不是從本地資源類。
在應用中創建視圖和引用它們的大衆模式是:
- 在佈局中定義視圖/部件,然後制定唯一的ID:
<Button android:id="@+id/my_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/my_button_text"/>
- 然後從佈局中獲取視圖對象的實例(一般在 onCreate() 方法中):
Button myButton = (Button) findViewById(R.id.my_button);
在創建 RelativeLayout 時爲視圖對象定義ID尤爲重要。在相對佈局裏,同級視圖可以通過唯一ID來引用其它的同級視圖,並相對與它們來定義自己的佈局。
ID不需要在整個樹中唯一,但是應該在你正在搜索的某部分樹中是唯一的(可能經常使用整個樹,所以最好儘可能的完全唯一)。
佈局參數
以layout_something命名的XML佈局屬性用來爲適合放入ViewGroup中的View定義佈局參數的。
每個ViewGroup類都有一個繼承了 ViewGroup.LayoutParams 的嵌套類。這些嵌套類包含一些用來定義ViewGroup內每個子View的大小和位置的屬性類型。正如你在圖1中看到的,父ViewGroup爲每個子View定義了佈局參數(包括子ViewGroup)。
圖1. 圖形化的帶有每個相關聯視圖的佈局參數的視圖層級。
注意每個LayoutParams子類都有自己的一套設置屬性值的語法。每個子元素必須定義適用於它父節點的LayoutParams,儘管它可能已爲自己的子接口定義不同的LayoutParams。
所有的ViewGroup都包含寬度和高度屬性(layout_width
和layout_height
),並且所有的視圖都需要定義它們。另外很多LayoutParams也包含可選的邊距和邊框。
你可以使用精確的尺寸來指定寬度和高度,儘管你通過可能不會這麼做。更多的時候,你會使用這些常量去設置寬度或高度:
- wrap_content 使你的視圖尺寸適配它內容需要的大小
- fill_parent (在API等級8中更名爲match_parent )使你的視圖與它的父ViewGroup允許的尺寸一樣大
一般而言,不提倡使用諸如像素的絕對單位來指定佈局的寬度和高度,而是更適合使用諸如密度無關像素(dp)的相對尺寸,wrap_content,或fill_parent,這樣可以保證你的應用在不同尺寸的屏幕上顯示正常。這些公認的尺碼類型在 Available Resources 中有明確說明。
佈局位置
視圖的幾何結構就是矩形。視圖有位置屬性,表現爲左上座標系,還有兩個尺寸,表現爲寬度和高度。位置和尺寸的單位是像素。
通過調用 getLeft()
和 getTop()
方法可以獲得視圖的位置,前一個方法返回視圖距離顯示視圖的矩形的左或X座標,後一個方法返回視圖距離顯示視圖的矩形的上或Y座標。這些方法返回的都是視圖相對於他們父節點的位置。例如,getLeft()返回20就表示這個視圖在距離它的直接父節點左邊緣的右邊20像素處。
此外,還提供了一些便利的方法來避免不必要的計算,也就是 getRight()
和 getBottom()
。這些方法返回視圖距離顯示視圖的矩形的右邊距座標和下邊距座標。例如,調用 getRight()
的結果與下面的計算結果相同:getLeft()
+ getWidth()
.
大小,內邊距,外邊距
視圖的大小通過寬度和高度來表現。視圖實際上具有兩組寬度和高度值。
第一組被稱爲測量寬度和測量高度。這些尺寸定義了視圖在它的父容器裏想要表現爲多大。這些測量尺寸可以通過調用 getMeasuredWidth()
和 getMeasuredHeight()
獲得。
第二組簡稱爲寬度和高度,有時也被稱爲繪製寬度和繪製高度。這些尺寸在繪製時或佈局後定義了視圖在屏幕上的實際大小。這些值可能但不一定和測量寬度和高度不同。這些寬度和高度可以通過調用 getWidth()
和 getHeight()
獲得。
測量視圖的尺寸就不得不考慮到它的內邊距。視圖的左,右,上,下部分的內邊距都是用像素表示的。可以給內邊距一個指定的像素值來偏移視圖的內容。例如,左側2個單位的內邊距會把視圖的內容從視圖的左邊界向右移動2個像素。可以用 setPadding(int,
int, int, int)
方法來設置內邊距,用 getPaddingLeft()
,getPaddingTop()
,getPaddingRight()
和 getPaddingBottom()
查詢內邊距。
儘管View可以定義內邊距,但是對於外邊距不提供任何支持,ViewGroup提供外邊距的支持。請參考 ViewGroup
和 ViewGroup.MarginLayoutParams
獲取更多信息。
請閱讀 Dimension Values 獲取更多關於尺寸的信息。
常用佈局
每種 ViewGroup
類的子類都提供了唯一一種顯示嵌入在其中的視圖的方式。下面是Android平臺內置的一些常用的佈局類型。
註解:儘管爲了達到你的UI設計效果你可以在一個佈局中嵌套一個或多個的佈局,你應該儘可能的保持淺的佈局層級。如果你的佈局裏的嵌套佈局比較少,那麼它的繪製速度就會很快(寬視圖層級比深視圖層級要好)。
使用適配器構建佈局
當你佈局的內容是動態的或者是非預設的時候,你可以使用 AdapterView 子類,這樣就能在運行時用視圖去填充佈局。AdapterView 類的子類使用 Adapter 把數據綁定到佈局上。Adapter
在數據源和 AdapterView
佈局中間扮演中間人的角色,Adapter 獲取數據(從諸如數組或數據庫查詢結果的數據源)並且把每個條目轉化成可以添加到 AdapterView 中的視圖。
依賴適配器的一般佈局包含:
用數據填充AdapterView
你可以通過把 AdapterView 的實例綁定上 Adapter 來填充諸如 ListView
或 GridView
的 AdapterView,Adapter 可以從外部數據源中獲取數據並且創建代表每個數據條目的 View。
Android提供了一些有用的 Adapter
子類來爲 AdapterView 獲取各種數據和構建視圖。最常見的是這兩種適配器:
ArrayAdapter
- 在你的數據源是一個數組時使用這種適配器。默認情況下,
ArrayAdapter
通過在每個數組項上調用 toString() 並把內容放置在 TextView 內爲每個數組項創建視圖。 -
例如,如果你想在 ListView 上顯示字符串數組,使用構造器實例化一個新的
ArrayAdapter
來爲字符數組和每個字符串指定佈局。ArrayAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray);
構造器的參數:
然後只需調用 ListView 的
setAdapter()
:ListView listView = (ListView) findViewById(R.id.listview); listView.setAdapter(adapter);
如果你想自定義每個項目的外觀,你可以爲數組內的對象重寫 toString() 方法。或者爲項目創建非 TextView 的視圖(例如,ImageView),然後繼承
ArrayAdapter
類,重寫getView()
爲每一項返回你想要類型的視圖。 SimpleCursorAdapter
- 在你的數據來自 Cursor 時使用這種適配器。使用
SimpleCursorAdapter
時,你必須爲 Cursor 中的每行指定一個佈局,Cursor 的列值應該被插入到該佈局中的視圖中。例如,如果你想創建一個包含用戶姓名和電話號碼的列表,你可以執行一個能返回包含每個用戶行和姓名號碼列的 Cursor 的查詢。然後你可以創建字符串數組來指定 Cursor 中的哪些列你想要出現在佈局中,創建整型數組來指定每一列放置在哪些相應的視圖上。 -
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; int[] toViews = {R.id.display_name, R.id.phone_number};
實例化 SimpleCursorAdapter 時,需要傳遞每項結果需要使用的佈局,包含結果的 Cursor 和這兩個數組:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0); ListView listView = getListView(); listView.setAdapter(adapter);
SimpleCursorAdapter
然後通過把每個fromColumns項目插入到相應的toViews視圖中爲使用指定佈局的 Cursor 中的每一行創建視圖。
假如你在應用生命週期的過程中改變了適配器使用的基本數據,那麼你應該調用 notifyDataSetChanged()
,這樣會通知關聯的視圖數據已經改變而且應該刷新自己。
捕獲點擊事件
通過實現 AdapterView.OnItemClickListener 接口你可以響應 AdapterView 中每個項目的點擊事件。例如:
// 創建一個捕獲消息的匿名類對象
private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position, long id) {
// 在這裏響應點擊事件
}
};
listView.setOnItemClickListener(mMessageClickedHandler);