iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0

前幾天介紹了好幾種UI基礎元件,你已經知道怎麼畫這些東西了,那該把他們畫在哪裡咧?
該是介紹 layout 出場的時候了

ViewGroup

前幾天介紹的 TextView、ImageView、EditText...等等,都是 View 類別的直接或間接子類別。

ViewGroup 是一抽象類別,顧名思義就是裝載這些 Views 或其他 ViewGroup 的隱形容器,雖不可見,卻影響著裡頭所有元件的版面配置(佈局),ViewGroup 是各式各樣的 layouts 和 views containers 的父類別(也稱超類、基類),UI 版面配置的方式都是由 layout定義。

layout 與裡面嵌著的元件(可能是 TextView、Button 等元件或另一個 layout)存在著階層關係。外層是父,中間夾著的 views 或其他ViewGroup 就是孩子。不會出現你的孩子不是你的孩子的問題。
Layout版面配置官方文檔

Layout

Android 提供了許多種不同的 layouts,而這些 layouts 對於元件都有各自擺放規則,讓我們可以依照需求靈活地完成各種構圖,而對用戶來說,並無法看見這個畫面使用了那些 layouts。這蠻好理解,每對父母都會有不同的養育規則,無形中影響著自己的孩子。layout有很多種,底下介紹比較常用到的 FrameLayout、 LinearLayout、ConstraintLayout。

FrameLayout

FrameLayout(禎佈局)是一種簡單好理解的 layout,單純將元件對齊 layout 左上角疊起,若裡面元件尺寸相同,那只會看到最頂層的元件。並且元件只能以 layout_gravity 來定義位置。

  • 如下例,元件樹中可以看到 FrameLayout 中明明有4個元件,但是我們畫布上只看得見按鈕及圖像。另外TextView、ImageButton 都被遮住了。
<FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:text="TextView"
            android:textSize="48dp"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:backgroundTint="#74FFFFFF"
            android:text="button"
            android:textColor="@color/teal_200"
            android:textSize="48sp"
            android:layout_gravity="center"/>
        <ImageButton
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:layout_gravity="bottom|center"
            android:contentDescription="@string/image_description"
            android:src="@drawable/child_face"/>
        <ImageView
            android:id="@+id/rabbit"
            android:layout_width="400dp"
            android:layout_height="500dp"
            android:background="#F0F4C0"
            android:src="@drawable/rabbit"
            android:contentDescription="@string/image_description"/>
    </FrameLayout>

  • 圖層由頂層到底的順序跟xml的順序是倒過來的,也就是在xml中,越接近結束標籤的代表是越頂層的,所以兔兔ImageView會把上面 TextVie 與ImageButton遮住。
  • 但是為何Button明明應該會被遮住卻沒有。Android SDK 21(即5.0)開始,Button自帶陰影的效果相當於是在z軸上提高了高度(如下圖所示),所以造成無法被其他 view 覆蓋。若想覆蓋Button,可在Button屬性加上 android:stateListAnimator="@null"。

註:stateListAnimator是可以讓View隨著狀態動起來的屬性。用法參考這篇有範例

運行模擬機時,使用 Layout Inspector 來做檢視,滑桿可控制圖層間距,使其更方便檢視。

LinearLayout

  • LinearLayout(線性佈局)對我來說,就像軍事化教育的父母,子元件都必須依照他指定的水平或垂直的方向進行排列,並且不可重疊。

  • 排列的方向則由orientation屬性決定,這個屬性會影響所有放在裡面的子元件。

    • 預設是水平排列(android:orientation = "horizontal")
      • 要注意元件的寬度不可設為match_parent,否則將會被單獨一個元件佔滿螢幕寬度,無法放下其他元件。
      • 若元件寬度加起來超出layout時,並不提供自動換到下一列的功能,可以看到預覽畫面的右側有虛線方框就是第三張小孩臉。(最後一句有種靈異照片的港覺...)
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:background="@color/teal_200"
        tools:context="com.tseng.theironmandemo.MainActivity2">
        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_margin="4dp"
            android:src="@drawable/child_face" />
        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_margin="4dp"
            android:background="#FFEB3B"
            android:src="@drawable/child_face" />
        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_margin="4dp"
            android:background="#FFEB3B"
            android:src="@drawable/child_face" />
    </LinearLayout>
    

    • 垂直方向排列(android:orientation = "vertical"),結構一樣只差:orientation不同就不放程式碼了。
      • 要注意元件的高度不可為 match_parent。
      • 若元件的高度加起來超出layout時,並不提供自動換到下一行的功能,第4張圖片被裁切。
  • 如果堅持都用LinearLayout做出格狀配置,就需要一行行或一列列嵌套。

不同底色是不同LinearLayout,上圖用了5個。官方不建議這樣處理,層層嵌套會影響介面效能。

ConstraintLayout

ConstraintLayout(約束佈局)就是放牛吃草的父母。
他也是預設的 layout,元件只需要1條水平約束線 + 1條垂直的約束線定位,可以很自由跟父層或附近的其他子元件約定好位置,可以用來繪製比較豐富複雜需要高自由度的排版,才不會layouts 一層嵌一層,而造成文件很複雜也可能影響繪製速度。

工作區域

  • 現在將編輯模式切到 Design 模式檢視,視窗主要分成三大塊

    • 左邊 : 調色盤 + 元件樹,可以從上半部調色盤選擇需要的元件拖曳到中間的工作區域或是下半部的元件樹,元件樹會標示警告或有問題的元件。有紅色驚嘆號的一定要去修復錯誤,否則編譯會報錯,黃色則是 IDE 可以忍受但建議改善的部分。
    • 中間 : 主要的編輯區域,可以拖拉元件調整位置,並且有豐富的工具列可以使用。
    • 右邊 : 屬性面板,會隨著開發者點選的元件切換,可以找到所需要的屬性作設定,跟純code的檔案是會連動一致。
  • 我覺得雖然ConstraintLayout也是以XML的型式來儲存,但是它又與其他的 layout 又有些區別,因為單純用程式碼模式編輯的話會覺得很麻煩,ConstraintLayout比較適合用Design模式來放定位拉好約束後,細節再用code來微調會比較省事。

  • 官方文檔:
    ConstraintLayout
    Build a Responsive UI with ConstraintLayout

小結

雖然XML的結構可以像俄羅斯娃娃多層嵌套下去,但是基於優化效能,官方文件建議,還是要以最簡單階層完成需求為目標,這樣繪製的速度才會快起來~
官方文檔 :
Optimizing layout hierarchies
效能與檢視區塊階層

設計工具列介紹

既然是新手友善系列,介紹一下工具列是需要的吧。
上排是通用的工具列,但 layout 不同,下排工具也會不同。
ConstraintLayout的下排工具比較複雜,用它當範本(下圖)

  1. 編輯中的layout檔名,設計橫向螢幕也是在這裡設定。如果你的 app 沒有限定使用直向模式的話,橫向螢幕也要編輯,否則螢幕翻轉時很可能就會跑版。
  2. 檢視設計圖或藍圖或兩者,還有色盲模式等。
  3. 直向或橫向螢幕,還有UI mode選擇normal或車用、手錶、平板、電視、VR headset...等模式。
  4. 非暗色或暗色模式。
  5. 選擇不同的型號、尺寸來預覽,手錶、手機、平板、車用設備到電視大小螢幕都有,拖曳app螢幕右下角來縮放也可以。
  6. API 版本:選取要預覽的 Android 版本。
  7. 套用應用程式主題預覽(theme)
  8. 針對strings的字串資源設定多國語言
  9. 選擇需要顯示的設計工具細節,比如:margin、約束線、顯示tool bar...等
  10. 開啟磁鐵功能的話是指,元件拖曳接近父層layout的時候,就會自動建立約束線。
  11. 元件的預設邊界值。
  12. 清除畫面上所有元件的約束,永遠都可以重新開始,真勵志啊~
  13. 推斷約束魔法棒,為所有元件自動建立約束,但是就不知道是驚喜還是驚嚇了。
  14. Guideline、Barrier、group...等輔助排版的工具都在這裡。
  15. 問題警告或報錯。
  16. 開啟 ConstraintLayout 設計工具的小幫手面板。

明天再更詳細一點介紹ConstraintLayout,打家明天見~


上一篇
第6天 UI基礎元件(四) : ProgressBar
下一篇
第8天 ConstraintLayout 詳細介紹(一)
系列文
新手向Android&Kotlin學習紀錄30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言