1. 程式人生 > Android入門教學 >Android 線性佈局 LinearLayout

Android 線性佈局 LinearLayout

1. LinearLayout 的特性

LinearLayout 繼承自 ViewGroup,可以將所包含的 View 按照線性方式一個一個的排列起來,即將 View 排列成一行(水平佈局)或者排列成一列(垂直佈局)。LinearLayout 有一個很關鍵的屬性:android:orientation,可以用它來設定佈局的方向,預設是橫向。

2. 常用設定

在編寫佈局程式碼之前,我們先來了解一下 LinearLayout 常用的設定項。

2.1 基本屬性:

  • id: 佈局唯一 id,用來在程式碼中通過 findViewById 查詢該佈局,獲取佈局物件
  • layout_height: 設定佈局高度,有三種可選值:
    • 具體高度(dp、px)
    • wrap_content: 佈局高度由子 View 的高度而定
    • match_parent: 佈局高度佔滿父佈局(等同於 fill_parent,後者已被廢棄,後文將直接使用 match_parent 替代 fill_parent)
  • layout_width: 設定佈局寬度,同 layout_height
  • layout_gravity: 設定佈局在其父佈局中的對齊方式,有以下幾種常用值:
    • top: 頂端對齊
    • bottom: 底部對齊
    • left: 居左對齊
    • right: 居右對齊
    • center: 居中對齊
      可以組合使用,比如left|top表示左上對齊
  • gravity: 設定佈局內的各個 View / Viewgroup 的對齊方式,使用方法同 layout_gravity
  • background: 設定佈局的背景樣式,可以用圖片或者顏色作為背景
  • layout_margin: 設定元素與周圍其他元素的間距,類似的還可以設定單邊的間距:
    • layout_marginRight
    • layout_marginTop
    • layout_marginLeft
    • layout_marginBottom

以上是大多數佈局都會有的屬性,在這一節講的相對詳細,後續出現可參考本節內容

2.2 特殊屬性

  • orientation: 線性佈局的方向,前面提到過可以用它決定內部 View 的排列方向。
  • layout_weight: 內部 View 的大小權重,這個是 LinearLayout 裡很重要的一個設定,它將 LinearLayout 內部的 View 按照一定比例分配大小,具體使用後面會詳細介紹
  • divider: 設定佈局之間的分割線,可以通過圖片指定樣式
  • dividerPadding: 分割線之間的間距
  • showDividers: 設定分割線的位置,有以下可選值:
    • beginning: 在元素之前展示分割線
    • end: 在元素之後展示分割線
    • middle: 在每個元素之間展示分割線
    • none: 不展示

3. 編寫垂直樣式佈局

線性佈局分為垂直和水平佈局兩種方式,在使用過程中除了方向不同,其餘類似。本節僅演示垂直樣式,水平樣式相信你能夠觸類旁通。
顧名思義,垂直佈局就是將內部 View 從上到下依次排成一列,為了便於理解,直接上程式碼,在我們新建的工程中,找到“res->layout->activity_main.xml”,編寫程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="Here"
        android:background="#E71B0C"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="Is"
        android:background="#E7430F"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="Imooc"
        android:background="#E6D11B"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:background="#88F10D"
        android:text="Android"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:background="#03A9F4"
        android:text="Study"/>

</LinearLayout>

直接編譯,效果如下:

垂直線性佈局

如圖,在螢幕中有 5 個 TextView 按照垂直方向依次排成一列。注意,Layout 都是繼承自 ViewGroup 的,在上一節我們說過 ViewGroup 也是 View,所以我們可以推理出 Layout 裡面也是可以放 Layout 的。按照這個邏輯我們還可以在垂直佈局中巢狀水平佈局,比如我們希望把“Here Is”和“Android Study”這兩個短語寫到一排:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E71B0C"
            android:text="Here"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E7430F"
            android:text="Is"
            android:textSize="30sp" />
    </LinearLayout>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#E6D11B"
        android:text="Imooc"
        android:textSize="30sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#88F10D"
            android:text="Android"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#03A9F4"
            android:text="Study"
            android:textSize="30sp" />
    </LinearLayout>

</LinearLayout>

直接執行:

LinearLayout巢狀使用

我們將“Here”和“Is”、“Android”和“Study”這四個 TextView 兩兩一組分別放到了一個水平樣式的 LinearLayout 中,這樣驗證了,Layout 是可以巢狀使用的。

4. weight 的使用

以上是 LinearLayout 排列方式的使用,接下來講到一個很關鍵的屬性——weight,它可以使內部 View 按照一定的比例配置尺寸,有同學可能會有疑問,前面不是有layout_heightlayout_width用來設定尺寸嗎?那它和layout_weight有什麼關係,會不會有什麼衝突?帶著這個疑問,一起學習 weight 的用法吧。

layout_weight是 LinearLayout 特有的一個屬性,它很好的利用了線性佈局的特點,讓系統自適應的幫我們完成比例縮放。和你想的一樣,它和layout_widthlayout_height密不可分,他們的相互影響,最終的尺寸有很多種計算方法。這裡提供一種我認為最簡單的理解:

先按照 layout_height / layout_width 的設定分配所需大小,然後剩下的空間按照 weight 的比例分配,最終加起來的尺寸就是各個 View 的最終尺寸。

關於 layout_height / layout_width 可以大致分為 3 種情況:

  • 高度 / 寬度設定為 0
  • 高度 / 寬度為 wrap_content
  • 高度 / 寬度為 match_parent
    以下就針對這三種情況詳細說明。

4.1 設定成 0 dp(重點)

這個是最直接,最常用的設定方式,也是我們需要掌握的重中之重。如果我們將高度設定成 0 dp,那麼系統就會直接使用 weight 的比值作為尺寸比例分配給各個子 View。我們直接在上面的程式碼中進一步修改,不考慮內嵌的 LinearLayout,對 3 個子 View 新增 weight 屬性,並加上背景色方便區分:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:background="#EBA2A2"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E71B0C"
            android:text="Here"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E7430F"
            android:text="Is"
            android:textSize="30sp" />
    </LinearLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:background="#E6D11B"
        android:text="Imooc"
        android:textSize="30sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:background="#AACCE7"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#88F10D"
            android:text="Android"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#03A9F4"
            android:text="Study"
            android:textSize="30sp" />
    </LinearLayout>

</LinearLayout>

效果如下:

layout_height="0dp"

可以看到,3 個子 View 的高度正好是按照 1:2:3 排列。按照上面給出的計算方法,各個View的高度是 0,所以直接就是按照比例排列。
將高度/寬度寫成 0 再使用 weight 屬性是最直接最簡單的方法,也是最常用最重要的方法,大家今後會經常用到,務必掌握!

4.2 設定成 wrap_content

我們將上面的程式碼中0dp直接修改成wrap_content再編譯,會發現樣式好像沒有變,比例貌似也是 1:2:3。注意,很多地方會解釋成wrap_content也是直接按照weight比例來分配,其實這是大錯特錯的
我們在截圖上加上標誌,仔細看看尺寸:

layout_height="wrap_content"

三個View的高度大約是 169、285、400,這個比例明顯不符合 1:2:3 ,那這個比例是如何計算的呢?
我們再來回顧一下weight計算方式的定義,首先我們根據wrap_content計算高度,那麼 3 個子 View 都是單行 size 相同的文字,所以本身高度一樣,剩下部分按照 1:2:3 來分配。那麼經過測量,單行高度是 54,我們將每個 View 的高度減去 54,得到剩餘高度:

第一個View的剩餘高度:169 - 54 = 115
第二個View的剩餘高度:285 - 54 = 231
第三個View的剩餘高度:400 - 54 = 346

這樣一來,剩餘的尺寸就剛好符合 1:2:3 了。

4.3 設定成 match_parent

match_parent的行為是最詭異的,但是如果理解了wrap_contentmatch_parent也就不難解釋,先來看看效果,我們將程式碼中的wrap_content替換成match_parent再來看看效果:

layout_height="match_parent"

我們會發現第三塊直接消失了,這又是為什麼呢?不要慌,我們還是套用一下定義。首先假定父佈局高度是 X,那麼 match_parent之後每個子View的高度都是 X,這樣再按照比例分割剩下的 X - 3X。所以可以得到 3 個子 View 的高度分別是:

第一個View的高度:X + 1/6 * (X-3X) = (2/3)X
第二個View的高度:X + 2/6 * (X-3X) = (1/3)X
第三個View的高度:X + 3/6 * (X-3X) = 0

經過計算,非常合理!

5. 小結

這是大家學習的第一個 Layout,所以對屬性的講解會多也更詳細,大家完全不必死記硬背,在今後熟悉了之後會發現其實大部分屬性都大同小異。對於 LinearLayout 還有一些其他屬性,比如前門提到過的 divider 等等,這個比較簡單也比較容易理解,大家完全可以作為課後練習編寫程式碼自行測試。
其實對於 LinearLayout 有很多的侷限性,比如它只能按照一行或者一列排列,如果我希望從多個方向去實現佈局, LinearLayout 就顯得很蹩腳了,接下來一章我們會介紹一種非常靈活的佈局,拭目以待。