一個豐富的介面總是要由很多個控制元件組成的,那我們如何才能讓各個控制元件都有條不紊地 擺放在介面上,而不是亂糟糟的呢?這就需要藉助佈局來實現了。佈局是一種可用於放置很 多控制元件的容器,它可以按照一定的規律調整內部控制元件的位置,從而編寫出精美的介面。當然, 佈局的內部除了放置控制元件外,也可以放置佈局,通過多層佈局的巢狀,我們就能夠完成一些 比較複雜的介面實現,示意圖 3.15 很好地展示了它們之間的關係。
圖 3.15
下面我們來詳細講解下 Android 中四種最基本的佈局。先做好準備工作,新建一個
UILayoutTest 專案,並讓 ADT 自動幫我們建立好活動,活動名和佈局名都使用預設值。
3.3.1 LinearLayout
LinearLayout 又稱作線性佈局,是一種非常常用的佈局。正如它名字所描述的一樣,這 個佈局會將它所包含的控制元件線上性方向上依次排列。相信你之前也已經注意到了,我們在上 一節中學習控制元件用法時,所有的控制元件就都是放在 LinearLayout 佈局裡的,因此上一節中的控 件也確實是在垂直方向上線性排列的。
既然是線性排列,肯定就不僅只有一個方向,那為什麼上一節中的控制元件都是在垂直方向 排列的呢?這是由於我們通過 android:orientation 屬性指定了排列方向是 vertical,如果指定 的是 horizontal ,控制元件就會在水平方向上排列了。下面我們通過實戰來體會一下,修改 activity_main.xml 中的程式碼,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >
<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button 1" />
<Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button 2" />
<Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button 3" />
</LinearLayout>
我們在 LinearLayout 中新增了三個 Button,每個 Button 的長和寬都是 wrap_content,並 指定了排列方向是 vertical。現在執行一下程式,效果如圖 3.16 所示。
圖 3.16
然後我們修改一下 LinearLayout 的排列方向,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" >
……
</LinearLayout>
將 android:orientation 屬性的值改成了 horizontal,這就意味著要讓 LinearLayout 中的控 件在水平方向上依次排列,當然如果不指定 android:orientation 屬性的值,預設的排列方向就 是 horizontal。重新執行一下程式,效果如圖 3.17 所示。
圖 3.17
這裡需要注意,如果 LinearLayout 的排列方向是 horizontal,內部的控制元件就絕對不能將 寬度指定為 match_parent,因為這樣的話單獨一個控制元件就會將整個水平方向佔滿,其他的控 件就沒有可放置的位置了。同樣的道理,如果 LinearLayout 的排列方向是 vertical,內部的控 件就不能將高度指定為 match_parent。
瞭解了 LinearLayout 的排列規律,我們再來學習一下它的幾個關鍵屬性的用法吧。 首先來看 android:layout_gravity 屬性,它和我們上一節中學到的 android:gravity 屬性看起來有些相似,這兩個屬性有什麼區別呢?其實從名字上就可以看出,android:gravity 是用
於指定文字在控制元件中的對齊方式,而 android:layout_gravity 是用於指定控制元件在佈局中的對齊 方 式 。 android:layout_gravity 的 可 選 值 和 android:gravity 差 不 多 , 但 是 需 要 注 意 , 當 LinearLayout 的排列方向是 horizontal 時,只有垂直方向上的對齊方式才會生效,因為此時水 平方向上的長度是不固定的,每新增一個控制元件,水平方向上的長度都會改變,因而無法指定 該方向上的對齊方式。同樣的道理,當 LinearLayout 的排列方向是 vertical 時,只有水平方 向上的對齊方式才會生效。修改 activity_main.xml 中的程式碼,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" >
<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top" android:text="Button 1" />
<Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="Button 2" />
<Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:text="Button 3" />
</LinearLayout>
由於目前 LinearLayout 的排列方向是 horizontal,因此我們只能指定垂直方向上的排列方 向,將第一個 Button 的對齊方式指定為 top,第二個 Button 的對齊方式指定為 center_vertical, 第三個 Button 的對齊方式指定為 bottom。重新執行程式,效果如圖 3.18 所示。
圖 3.18
接下來我們學習下 LinearLayout 中的另一個重要屬性,android:layout_weight。這個屬性 允許我們使用比例的方式來指定控制元件的大小,它在手機螢幕的適配性方面可以起到非常重要 的作用。比如我們正在編寫一個訊息傳送介面,需要一個文字編輯框和一個傳送按鈕,修改 activity_main.xml 中的程式碼,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" >
<EditText android:id="@+id/input_message" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="Type something"/>
<Button android:id="@+id/send" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Send"/>
</LinearLayout>
你會發現,這裡竟然將 EditText 和 Button 的寬度都指定成了 0,這樣文字編輯框和按鈕 還能顯示出來嗎?不用擔心,由於我們使用了 android:layout_weight 屬性,此時控制元件的寬度 就不應該再由 android:layout_width 來決定,這裡指定成 0 是一種比較規範的寫法。
然後我們在 EditText 和 Button 裡都將 android:layout_weight 屬性的值指定為 1,這表示EditText 和 Button 將在水平方向平分寬度,重新執行下程式,你會看到如圖 3.19 所示的效果。
圖 3.19
為什麼將 android:layout_weight 屬性的值同時指定為 1 就會平分螢幕寬度呢?其實原理 也很簡單,系統會先把 LinearLayout 下所有控制元件指定的 layout_weight 值相加,得到一個總值, 然後每個控制元件所佔大小的比例就是用該控制元件的 layout_weight 值除以剛才算出的總值。因此如 果想讓 EditText 佔據螢幕寬度的 3/5 ,Button 佔據螢幕寬度的 2/5 ,只需要將 EditText 的 layout_weight 改成 3,Button 的 layout_weight 改成 2 就可以了。
我 們 還 可 以 通 過 指 定 部 分 控 件 的 layout_weight 值 , 來 實 現 更 好 的 效 果 。 修 改
activity_main.xml 中的程式碼,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" >
<EditText android:id="@+id/input_message" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="Type something"/>
<Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send"/>
</LinearLayout>
這裡我們僅指定了 EditText 的 android:layout_weight 屬性,並將 Button 的寬度改回 wrap_content。這表示 Button 的寬度仍然按照 wrap_content 來計算,而 EditText 則會佔滿屏 幕所有的剩餘空間。使用這種方式編寫的介面,不僅在各種螢幕的適配方面會非常好,而且 看起來也更加舒服,重新執行程式,效果如圖 3.20 所示。
圖 3.20
3.3.2 RelativeLayout
RelativeLayout 又稱作相對佈局,也是一種非常常用的佈局。和 LinearLayout 的排列規 則不同,RelativeLayout 顯得更加隨意一些,它可以通過相對定位的方式讓控制元件出現在佈局 的任何位置。也正因為如此,RelativeLayout 中的屬性非常多,不過這些屬性都是有規律可 循的,其實並不難理解和記憶。我們還是通過實踐來體會一下,修改 activity_main.xml 中的 程式碼,如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" >
<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="Button 1" />
<Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:text="Button 2" />
<Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Button 3" />
<Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" android:text="Button 4" />
<Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:text="Button 5" />
</RelativeLayout>
我想以上程式碼已經不需要我再做過多解釋了,因為實在是太好理解了,我們讓 Button 1 和父佈局的左上角對齊,Button 2 和父佈局的右上角對齊,Button 3 居中顯示,Button 4 和父 佈局的左下角對齊,Button 5 和父佈局的右下角對齊。雖然 android:layout_alignParentLeft、 android:layout_alignParentTop、android:layout_alignParentRight、android:layout_alignParentBottom、 android:layout_centerInParent 這幾個屬性我們之前都沒接觸過,可是它們的名字已經完全說 明瞭它們的作用。重新執行程式,效果如圖 3.21 所示。
圖 3.21
上面例子中的每個控制元件都是相對於父佈局進行定位的,那控制元件可不可以相對於控制元件進行
定位呢?當然是可以的,修改 activity_main.xml 中的程式碼,如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" >
<Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Button 3" />
<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/button3" android:layout_toLeftOf="@id/button3" android:text="Button 1" />
<Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/button3" android:layout_toRightOf="@id/button3" android:text="Button 2" />
<Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/button3" android:layout_toLeftOf="@id/button3" android:text="Button 4" />
<Button android:id="@+id/button5"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/button3" android:layout_toRightOf="@id/button3" android:text="Button 5" />
</RelativeLayout>
這次的程式碼稍微複雜一點,不過仍然是有規律可循的。android:layout_above 屬性可以讓 一個控制元件位於另一個控制元件的上方,需要為這個屬性指定相對控制元件 id 的引用,這裡我們填入 了 @id/button3 , 表 示 讓 該 控 件 位 於 Button 3 的 上 方 。 其 他 的 屬 性 也 都 是 相 似 的 , android:layout_below 表示讓一個控制元件位於另一個控制元件的下方,android:layout_toLeftOf 表示讓 一個控制元件位於另一個控制元件的左側,android:layout_toRightOf 表示讓一個控制元件位於另一個控制元件 的右側。注意,當一個控制元件去引用另一個控制元件的 id 時,該控制元件一定要定義在引用控制元件的後 面,不然會出現找不到 id 的情況。重新執行程式,效果如圖 3.22 所示。
圖 3.22
RelativeLayout 中還有另外一組相對於控制元件進行定位的屬性,android:layout_alignLeft 表 示讓一個控制元件的左邊緣和另一個控制元件的左邊緣對齊,android:layout_alignRight 表示讓一個控制元件的右邊緣和另一個控制元件的右邊緣對齊,還有 android:layout_alignTop 和 android:layout_alignBottom,道理都是一樣的,我就不再多說,這幾個屬性就留給你自己去嘗試一下了。 好了,正如我前面所說,RelativeLayout 中的屬性雖然多,但都是有規律可循的,所以學起來一點都不覺得吃力吧?
3.3.3 FrameLayout
FrameLayout 相比於前面兩種佈局就簡單太多了,因此它的應用場景也少了很多。這種 佈局沒有任何的定位方式,所有的控制元件都會擺放在佈局的左上角。讓我們通過例子來看一看 吧,修改 activity_main.xml 中的程式碼,如下所示:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"
>
<Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button"
/>
<ImageView android:id="@+id/image_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher"
/>
</FrameLayout>
FrameLayout 中只是放置了一個按鈕和一張圖片,重新執行程式,效果如圖 3.23 所示。
圖 3.23
可以看到,按鈕和圖片都是位於佈局的左上角。由於圖片是在按鈕之後新增的,因此圖 片壓在了按鈕的上面。
你可能會覺得,這個佈局能有什麼作用呢?確實,它的應用場景並不多,不過在下一章 中介紹碎片的時候,我們還是可以用到它的。
3.3.4 TableLayout
TableLayout 允許我們使用表格的方式來排列控制元件,這種佈局也不是很常用,你只需要 瞭解一下它的基本用法就可以了。既然是表格,那就一定會有行和列,在設計表格時我們 儘量應該讓每一行都擁有相同的列數,這樣的表格也是最簡單的。不過有時候事情並非總會 順從我們的心意,當表格的某行一定要有不相等的列數時,就需要通過合併單元格的方式來 應對。
比 如 我 們 正 在 設 計 一 個 登 錄 界 面 , 允 許 用 戶 輸 入 賬 號 密 碼 後 登 錄 , 就 可 以 將
activity_main.xml 中的程式碼改成如下所示:
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" >
<TableRow>
<TextView android:layout_height="wrap_content" android:text="Account:" />
<EditText android:id="@+id/account" android:layout_height="wrap_content" android:hint="Input your account" />
</TableRow>
<TableRow>
<TextView android:layout_height="wrap_content" android:text="Password:" />
<EditText android:id="@+id/password" android:layout_height="wrap_content" android:inputType="textPassword" />
</TableRow>
<TableRow>
<Button android:id="@+id/login" android:layout_height="wrap_content" android:layout_span="2" android:text="Login" />
</TableRow>
</TableLayout>
在 TableLayout 中每加入一個 TableRow 就表示在表格中新增了一行,然後在 TableRow 中每加入一個控制元件,就表示在該行中加入了一列,TableRow 中的控制元件是不能指定寬度的。 這裡我們將表格設計成了三行兩列的格式,第一行有一個 TextView 和一個用於輸入賬號的 EditText , 第 二 行 也 有 一 個 TextView 和 一 個 用 於 輸 入 密 碼 的 EditText , 我 們 通 過 將 android:inputType 屬性的值指定為 textPassword,把 EditText 變為密碼輸入框。可是第三行只有一個用於登入的按鈕,前兩行都有兩列,第三行只有一列,這樣的表格就會很難看,而且
結 構 也 非 常 不 合 理 。 這 時 就 需 要 通 過 對 單 元 格 進 行 合 並 來 解 決 這 個 問 題 , 使 用 android:layout_span="2"讓登入按鈕佔據兩列的空間,就可以保證表格結構的合理性了。重新 執行程式,效果如圖 3.24 所示。
圖 3.24
不過從圖中可以看出,當前的登入介面並沒有充分利用螢幕的寬度,右側還空出了一塊 區 域 , 這 也 難 怪 , 因 為 在 TableRow 中 我 們 無 法 指 定 控 件 的 寬 度 。 這 時 使 用 android:stretchColumns 屬性就可以很好地解決這個問題,它允許將 TableLayout 中的某一列 進行拉伸,以達到自動適應螢幕寬度的作用。修改 activity_main.xml 中的程式碼,如下所示:
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"
android:stretchColumns="1"
>
……
</TableLayout>
這裡將 android:stretchColumns 的值指定為 1,表示如果表格不能完全佔滿螢幕寬度,就將第二列進行拉伸。沒錯!指定成 1 就是拉伸第二列,指定成 0 就是拉伸第一列,不要以為
這裡我寫錯了哦。重新執行程式,效果如圖 3.25 所示。
圖 3.25