為 Android 建立 XML 版面配置

1. 事前準備

在本程式碼研究室中,您將為基本的小費計算機應用程式建構版面配置。在程式碼研究室結束時,應用程式的使用者介面已可運作,但還不會實際計算小費。以下程式碼研究室將說明如何讓應用程式正常運作且表現更專業。

必要條件

  • 可透過 Android Studio 中的範本建立及執行 Android 應用程式

課程內容

  • 如何在 Android 中讀取及寫入 XML 版面配置
  • 如何建構簡易表單,用於使用者文字輸入和選項

建構項目

  • 小費計算機 Android 應用程式適用的使用者介面

需求條件

2. 啟動專案

使用 Google 提供的小費計算機:https://www.google.com/search?q=tip+calculator

18da3c120daa0759.png

在這個途徑中,您應該使用 Android 應用程式建立簡易的計算機簡易版。

開發人員通常就是採用這樣的做法作業:取得簡易版本的應用程式,並讓應用程式正常運作 (即使看起來不太好),之後仍能流暢地使用這些功能和影視內容。

完成程式碼研究室後,您的小費計算機應用程式會如下所示:

bcc5260318477c14.png

您需要使用 Android 提供的下列 UI 元素:

  • EditText - 用於輸入及傳送訊息
  • TextView - 顯示服務條款和小費金額等文字
  • RadioButton:每個小費選項的可選取圓形按鈕
  • RadioGroup - 將圓形按鈕選項分組
  • Switch - 開啟/關閉切換按鈕,選擇是否將小費四捨五入

建立一個空白活動專案

  1. 首先,在 Android Studio 中使用 空白活動 範本來建立新品的 Kotlin 專案。
  2. 呼叫應用程式「提示時間」,最低 API 級別為 19 (KitKat)。套件名稱為 com.example.tiptime

4f7619e9faff20e9.png

  1. 按一下「Finish」即可建立應用程式。

3. 讀取及瞭解 XML

如果不想使用自己熟悉的版面配置編輯器,您可以修改描述使用者介面的 XML 來建構應用程式的版面配置。瞭解使用 XML 瞭解及修改 UI 版面配置對 Android 開發人員的重要性。

您將會查看並編輯 XML 檔案,為這個應用程式定義 UI 版面配置。XML 是可延伸標記語言的一種,這是一種利用文字文件來描述資料的方式。由於 XML 是可擴充且極具彈性的功能,所以有許多種不同的用途,包括定義 Android 應用程式的 UI 版面配置。您或許可以在先前的程式碼研究室中回想起,「strings.xml」這個 XML 檔案中的其他資源也定義了其他資源。

Android 應用程式的使用者介面是由元件(小工具)的元件階層組成,以及這些元件的螢幕版面配置。請注意,這些版面配置本身是 UI 元件。

您必須說明畫面上的 UI 元素檢視畫面階層。舉例來說,ConstraintLayout (父項) 可包含 ButtonsTextViewsImageViews 或其他檢視畫面 (子項)。請注意,ConstraintLayoutViewGroup 的子類別。可讓您靈活設定子項的位置或調整大小。

74c7c563d18fffd4.png

Android 應用程式的內含項目階層

32df120272b2331d.png

每個 UI 元素都會在 XML 檔案中以 XML 元素出現。每個元素的開頭和結尾都是代碼,而每個標記的開頭都是 <,結尾則是 >。就像您可以使用版面配置編輯器 (設計) 設定 UI 元素的屬性,XML 元素也可以具有「屬性」。簡單來說,上述 UI 元素的 XML 可能如下所示:

<ConstraintLayout>
    <TextView
        text="Hello World!">
    </TextView>
</ConstraintLayout>

8dea708333aebabe.png

以下是一個實際範例。

  1. 開啟 activity_main.xml (「app」>「res」>「layout」>「activity_main.xml」)
  2. 您可能會注意到應用程式顯示包含「Hello World!」的 TextView。如 ConstraintLayout 中所述,您在使用這個範本建立的先前的專案中看過。

4fbdb64c02d62e73.png

  1. 在版面配置編輯器的右上方,找到「Code」、「Split」和「Design」檢視畫面的選項。
  2. 選取「Code」檢視。

6203bec920791bcc.png

activity_main.xml 中的 XML 如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

其中有簡單的說明比簡單的範例來得好,但 Android Studio 有一些努力能讓 XML 更易於閱讀,就像使用 Kotlin 程式碼一樣。

  1. 請注意縮排。Android Studio 會自動執行上述作業,顯示元素階層。TextView 已縮排,因為 ConstraintLayout 裡包含這個項目。ConstraintLayout 是父項,TextView 是子節點。每個元素的屬性會以縮排的方式顯示,表示該元素屬於該元素的一部分。
  2. 請注意色彩編碼:有些項目是藍色,部分是綠色,依此類推。檔案的類似部分會繪製成同一顏色,方便您比對。請特別注意,Android Studio 會繪製用相同顏色元素的開始和結尾。(注意:程式碼研究室所用的顏色可能會與 Android Studio 中顯示的顏色不符)。

XML 標記、元素和屬性

以下是 TextView 元素的簡化版本,方便您查看部分重要部分:

<TextView
    android:text="Hello World!"
/>

包含 <TextView 的行則是標記的開頭,而 /> 行則是標記結尾。標有 android:text="Hello World!" 的行是標記的屬性。這代表 TextView 將要顯示的文字。這 3 行是常用的簡寫,又稱為「空白元素標記」。也就是說,如果您利用獨立的「start-tag」和「end-tag」編寫程式碼,例如:

<TextView
    android:text="Hello World!"
></TextView>

另一個常見的例子是空白元素標記,編寫時盡可能減少這類標記的行數,然後將代碼的結尾與行前的行結合。因此,您可能會在兩行中看到空白元素標記 (沒有屬性的話,甚至只有一行):

<!-- with attributes, two lines -->
<TextView
    android:text="Hello World!" />

由於 ConstraintLayout 元素是以獨立開始和結束標記寫成,因此必須含有其他元素。以下是含有 TextView 元素的 ConstraintLayout 元素簡化版本:

<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView
        android:text="Hello World!" />
</androidx.constraintlayout.widget.ConstraintLayout>

如要將另一個 View 新增為 ConstraintLayout 的子項 (例如 TextView 下的 Button),其會在 TextView 標記 /> 的結尾,並且在 ConstraintLayout 的結尾標記之前,如下所示:

<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView
        android:text="Hello World!" />
    <Button
        android:text="Calculate" />
</androidx.constraintlayout.widget.ConstraintLayout>

進一步瞭解版面配置的 XML

  1. 查看 ConstraintLayout 的標記,您會發現這顯示為 androidx.constraintlayout.widget.ConstraintLayout,而非和 TextView 一樣只有 ConstraintLayout。這是因為 ConstraintLayout 是 Android Jetpack 的一部分,而其中的程式碼程式庫在 Android 核心平台之外提供了額外功能。Jetpack 提供了一些實用功能,可協助您輕鬆建構應用程式。您會發現此 UI 元件因為是以「androidx」開頭,所以屬於 Jetpack 的一部分。
  2. 您可能會發現開頭為 xmlns: 的行數,後接 androidapptools
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"

xmlns 代表 XML 命名空間,而每一行分別定義一個「結構定義」或相關詞彙。舉例來說,android: 命名空間會標示 Android 系統定義的屬性。版面配置 XML 中的所有屬性都是從其中一個命名空間開始執行。

  1. XML 元素之間的空白字元不會對電腦產生意義,但可讓 XML 更易於閱讀。

Android Studio 會自動加入一些空白字元和縮排,為了方便閱讀。在稍後說明,我們會說明如何讓 Android Studio 確保您的 XML 採用程式碼樣式慣例。

  1. 您可以對 XML 新增註解,就跟使用 Kotlin 程式碼一樣。開頭是 <!--,結尾是-->
<!-- this is a comment in XML -->

<!-- this is a
multi-line
Comment.
And another
Multi-line comment -->
  1. 請注意檔案的第一行:
<?xml version="1.0" encoding="utf-8"?>

用於表示檔案是 XML 檔案,但並非所有 XML 檔案都會包含這類檔案。

4. 在 XML 中製作版面配置

  1. activity_main.xml 中,請切換至「Split」 畫面,即可查看 設計編輯器 旁的 XML。「設計編輯器」可讓您預覽 UI 版面配置。

a03bcf5beacb4b45.png

  1. 您可以自行決定要使用哪個方式檢視,但是在這個程式碼研究室中,請使用「Split」檢視,這樣您才可以同時查看您編輯的 XML,以及在「Design Editor」中編輯所帶來的變更。
  2. 請嘗試點選其他行 (ConstraintLayout 下和 TextView 下一行),然後可注意到會選取到「Design Editor」中對應的畫面。反之亦然。舉例來說,如果您在「Design Editor」中按一下 TextView,系統就會醒目顯示對應的 XML。

1abc54a646c39f66.png

刪除 TextView

  1. 您現在不需要 TextView,請將其刪除。請務必從 <TextView/> 結尾全部刪除。
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

檔案還剩 ConstraintLayout

<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".MainActivity">

</androidx.constraintlayout.widget.ConstraintLayout>
  1. ConstraintLayout 中加入 16dp 邊框間距,避免 UI 與螢幕邊緣過度擁擠。

邊框間距與邊界相似,但會在 ConstraintLayout 內部增加空格,而不是在外部增加空格。

<androidx.constraintlayout.widget.ConstraintLayout
    ...
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

新增服務費用文字欄位

在這個步驟中,您必須新增 UI 元素,才能在應用程式中輸入服務費用。您必須使用 EditText 元素,讓使用者在應用程式中輸入或修改文字。

7746dedb0d79923f.png

  1. 請參閱 EditText 說明文件,並檢視 XML 範例。
  2. 找出 ConstraintLayout 開頭和結尾標記之間的空白區塊。
  3. 將說明文件中的 XML 複製並貼到 Android Studio 的版面配置中。

您的版面配置檔案應該會如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/plain_text_input"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:inputType="text"/>

</androidx.constraintlayout.widget.ConstraintLayout>

目前您可能不清楚,但會在下列步驟中進一步說明。

  1. 請注意,EditText 會加上紅色底線。
  2. 將滑鼠游標移到指標上,系統就會顯示「檢視畫面不受限」的錯誤訊息,您對在舊版程式碼研究室中的這類程式碼應該十分熟悉。先前提過,ConstraintLayout 的子項設有限制,因此版面配置會熟悉該如何排列。

40c17058bd6786f.png

  1. 將這些限制新增至 EditText 可固定在父項的左上角。
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

如果使用英文或其他由左至右的語言 (LTR),則起始點在左側。不過,部分語言 (例如阿拉伯字母) 的書寫順序是由右至左 (RTL),因此開頭應在右側。因此,該限制使用「start」,以便與 LTR 或 RTL 語言搭配使用。同樣地,限制都會使用「end」而不是右方。

加入新限制條件後,EditText 元素會如下所示:

<EditText
    android:id="@+id/plain_text_input"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:inputType="text"/>

查看編輯文字屬性

請仔細檢查您貼上的所有 EditText 屬性,確保它們與您應用程式中的方式搭配運作。

  1. 找出設為 @+id/plain_text_inputid 屬性。
  2. id 屬性變更為較為適當的名稱 @+id/cost_of_service
  1. 查看 layout_height 屬性。已設為 wrap_content,表示高度會和當中的內容高度相同。沒關係,因為只有 1 行文字。
  2. 查看 layout_width 屬性。已設為 match_parent,但你無法在 ConstraintLayout 的子項中設定 match_parent。此外,文字欄位不需要那麼寬。設為 160dp 的固定寬度,但應預留足夠空間,讓使用者輸入服務費用。

1f82a5e86ae94fd2.png

  1. 找到新的 inputType 屬性,這是一項新功能。屬性值為 "text",表示使用者可在畫面上的欄位中輸入任何文字字元 (英數字元、符號等)。
android:inputType="text"

不過,假如您只想在 EditText 中輸入數字,因為該欄位代表貨幣金額。

  1. 清除 text 字詞,但保留引號。
  2. 輸入 number 的位置。輸入「n」後,Android Studio 會顯示包含「n」的可能完成項目清單。

99b04cbd21e74693.gif

  1. 選擇 numberDecimal,這個類型可限制只顯示含小數點的數字。
android:inputType="numberDecimal"

如要查看輸入類型的其他選項,請參閱開發人員說明文件中的 指定輸入法類型

因此,建議您再做些修改,因為這能夠在使用者輸入欄位中的提示內容,所以很實用。

  1. EditText 中加入 hint 屬性,藉此說明使用者應在欄位中輸入的內容。
android:hint="Cost of Service"

您也會看到「Design Editor」將其更新。

824454d2a316efb1.png

  1. 在模擬器中執行應用程式。看起來應該像這樣:

c9d413de53b0853d.png

做得好!這項服務尚未處理完成,但您已經有良好的第一步,並編輯了部分 XML。版面配置的 XML 應如下所示。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:layout_width="160dp"
        android:layout_height="wrap_content"
        android:hint="Cost of Service"
        android:inputType="numberDecimal"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

新增服務問題

在這個步驟中,您必須新增 TextView 問題說明:“服務如何?“請嘗試輸入,不要複製/貼上。你可以透過 Android Studio 提供建議的做法。

  1. 關閉 EditText 標記 /> 後,新增一行然後開始輸入 <TextView
  2. 從建議中選取 TextView,Android Studio 就會自動為 TextView 新增 layout_widthlayout_height 屬性。
  3. 請為這兩個屬性選擇 wrap_content,因為 TextView 只需要與當中的文字內容一樣大。fee18cc43df85294.png
  4. 新增含有 "How was the service?"text 屬性
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="How was the service?"
  1. 使用 /> 關閉標記。
  2. 請注意,在「Design Editor」TextView 會與 EditText 重疊。

ac09d5cae6ae2455.png

這樣看起來並不正確,因此請在接下來的 TextView 中加入限制。想想您需要哪些限制。TextView 應水平和垂直放置在哪些位置?您可參考下方的應用程式螢幕截圖瞭解適當的位置。

bcc5260318477c14.png

就垂直位置來說,TextView 應低於服務費用文字欄位。水平指定您要讓 TextView 對齊父項的邊緣。

  1. TextView 加上水平限制,限制它的起點到父項的起點。
app:layout_constraintStart_toStartOf="parent"
  1. TextView 中加入垂直限制,將 TextView 的上緣限制在服務費用 View 的底部邊緣。
app:layout_constraintTop_toBottomOf="@id/cost_of_service"

請注意,@id/cost_of_service 中沒有加號,因為 ID 已定義。

3822136f7ed815f2.png

這個部分還目前為止看起來還不夠好,但不用擔心。我們要確保所有必要畫面都顯示在這裡,且功能可以正常運作。您將在下列程式碼研究室中修正這個問題。

  1. TextView 新增資源 ID。您稍後必須參考這個資料檢視,因為我們建議您新增更多檢視點,並將這些項目互相衝突。
android:id="@+id/service_question"

此時,您的 XML 應如下所示。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:hint="Cost of Service"
        android:layout_height="wrap_content"
        android:layout_width="160dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:inputType="numberDecimal"/>

    <TextView
        android:id="@+id/service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="How was the service?"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service"/>

</androidx.constraintlayout.widget.ConstraintLayout>

5. 新增提示選項

接下來,您需要為各個使用者選項提供圓形按鈕選項。

共有三個選項:

  • 極佳 (20%)
  • 不錯 (18%)
  • 一般 (15%)

如果您不確定該如何操作,可以使用 Google 搜尋。這是開發人員在卡車時會用到的工具。

  1. 在 Google 上搜尋「radio button android」。最理想的搜尋結果為 Android 開發人員網站提供的指南,瞭解如何使用圓形按鈕。

f5f1c6778ae7a5d.png

  1. 瀏覽圓形按鈕指南

閱讀說明後,您可以確認可在自己的版面配置中,針對每個所需的圓形按鈕使用 RadioButton UI 元件。此外,您還必須在 RadioGroup 中將圓形按鈕分組,因為一次只能選取一個選項。

部分 XML 似乎符合您的需求。仔細閱讀,瞭解 RadioGroup 是父項檢視,以及 RadioButtons 的子項檢視。

  1. 返回 Android Studio 中的版面配置,將 RadioGroupRadioButton 新增至應用程式。
  2. TextView 元素之後,仍顯示在 ConstraintLayout 中,開始輸入 <RadioGroup。Android Studio 會提供實用的建議來協助您完成 XML。aee75ba409dc51aa.png
  3. RadioGrouplayout_widthlayout_height 設為 wrap_content
  4. 新增設為 @+id/tip_options 的資源 ID。
  5. 使用 > 關閉起始標記。
  6. Android Studio 會加上 </RadioGroup>。和 ConstraintLayout 一樣,RadioGroup 元素內部也會包含其他元素,因此建議您將元素移到單獨的線條。將「layout_width」和「layout_height」都設為「wrap_content」
  7. RadioGroup 限制在服務問題下方 (垂直),並限制父項的開頭 (水平)。
  8. android:orientation 屬性設為 vertical。如要指定某列的 RadioButtons,可以將方向設為 horizontal

RadioGroup 的 XML 應如下所示:

<RadioGroup
    android:id="@+id/tip_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/service_question">

</RadioGroup>

新增圓形按鈕

  1. RadioGroup 的最後一個屬性後方,在 </RadioGroup> 結束標記之前加上 RadioButton
<RadioGroup
    android:id="@+id/tip_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/service_question">

    <!-- add RadioButtons here -->

</RadioGroup>
  1. layout_widthlayout_height 設為 wrap_content
  2. @+id/option_twenty_percent 的資源 ID 指派給 RadioButton
  3. 將文字設為 Amazing (20%)
  4. 使用 /> 關閉標記。
<RadioGroup
   android:id="@+id/tip_options"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   app:layout_constraintTop_toBottomOf="@id/service_question"
   app:layout_constraintStart_toStartOf="parent"
   android:orientation="vertical">

   <RadioButton
       android:id="@+id/option_twenty_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Amazing (20%)" />

</RadioGroup>

53cb416b368e9612.png

您已新增一個 RadioButton,是否可以修改 XML,為 Good (18%)Okay (15%) 選項新增 2 個圓形按鈕?

以下是 RadioGroupRadioButtons 的 XML 外觀:

<RadioGroup
   android:id="@+id/tip_options"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   app:layout_constraintTop_toBottomOf="@id/service_question"
   app:layout_constraintStart_toStartOf="parent"
   android:orientation="vertical">

   <RadioButton
       android:id="@+id/option_twenty_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Amazing (20%)" />

   <RadioButton
       android:id="@+id/option_eighteen_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Good (18%)" />

   <RadioButton
       android:id="@+id/option_fifteen_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Okay (15%)" />

</RadioGroup>

bab25b6a35d4ce52.png

新增預設選項

目前未選取任何提示選項。系統預設會選取其中一個圓形按鈕選項。

RadioGroup 上有一個屬性可讓您指定要先檢查哪一個按鈕。名稱是 checkedButton,並設為所選圓形按鈕的按鈕資源

  1. RadioGroup 上,將 android:checkedButton 屬性設為 @id/option_twenty_percent
<RadioGroup
   android:id="@+id/tip_options"
   android:checkedButton="@id/option_twenty_percent"
   ...

請注意到「Design Editor」中的版面配置已更新。根據預設,系統會選取 20% 的小費選項!現在這開始看起來像小費計算機了!

c412e7f16590cd33.png

XML 目前看起來會像這樣:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:hint="Cost of Service"
        android:layout_height="wrap_content"
        android:layout_width="160dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:inputType="numberDecimal"/>

    <TextView
        android:id="@+id/service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="How was the service?"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service"
        app:layout_constraintStart_toStartOf="parent" />

    <RadioGroup
        android:id="@+id/tip_options"
        android:checkedButton="@id/option_twenty_percent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/service_question"
        app:layout_constraintStart_toStartOf="parent"
        android:orientation="vertical">

        <RadioButton
            android:id="@+id/option_twenty_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Amazing (20%)" />

        <RadioButton
            android:id="@+id/option_eighteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Good (18%)" />

        <RadioButton
            android:id="@+id/option_fifteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Okay (15%)" />
    </RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>

6. 完成版面配置的其餘部分

您現在已進入版面配置的最後一個部分。請新增 SwitchButtonTextView 以顯示小費金額。

bcc5260318477c14.png

新增切換鈕,讓小費四捨五入

接下來,您將使用 Switch 小工具,讓使用者選取「Yes」或「No」是否要將小費四捨五入。

您想要 Switch 的寬度和父項一樣,因此您可能會認為寬度應該設為 match_parent。如前文所述,您無法在 ConstraintLayout 的 UI 元素上設定 match_parent。相反地,您需要限制檢視畫面的開始和結束時間,並將寬度設為 0dp。將寬度設為 0dp 可讓系統不要計算寬度,只要嘗試與檢視畫面中的限制條件相符即可。

  1. RadioGroup 的 XML 後方加入 Switch 元素。
  2. 如上所述,將 layout_width 設為 0dp
  3. layout_height 設為 wrap_content 這會將 Switch 觀看內容與內部內容高度一樣。
  4. id 屬性設為 @+id/round_up_switch
  5. text 屬性設為 Round up tip?。這會做為 Switch 的標籤使用。
  6. Switch 的起始點限制為 tip_options 的起始邊緣,並將結尾點限制為父項的終點邊緣。
  7. Switch 的頂端限制在 tip_options 的底部。
  8. 使用 /> 關閉標記。

如果切換按鈕預設為開啟,且有 android:checked 的屬性,則可能的值為 true (開啟) 或 false (關閉)。

  1. android:checked 屬性設為 true

總而言之,Switch 元素的 XML 看起來會像這樣:

<Switch
    android:id="@+id/round_up_switch"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:checked="true"
    android:text="Round up tip?"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="@id/tip_options"
    app:layout_constraintTop_toBottomOf="@id/tip_options" />

d374fab984650296.png

新增計算按鈕

接著,您必須新增 Button 來向使用者說明小費的計算方式。您想將按鈕設為與上層最寬,因此水平限制和寬度與 Switch 相同。

  1. Switch 後方加上 Button
  2. 請將寬度設為 0dp,方法與 Switch 相同。
  3. 將高度設為 wrap_content
  4. 為資源 ID @+id/calculate_button 輸入文字,格式為「"Calculate"」。
  5. Button 的上方邊緣固定到 圓頭提示的底部邊緣?Switch.
  6. 將起始邊緣限制為上層的起點,以及將結尾邊緣設為父項的終點邊緣。
  7. 使用 /> 關閉標記。

以下是「Calculate」Button 的 XML 內容:

<Button
   android:id="@+id/calculate_button"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="Calculate"
   app:layout_constraintTop_toBottomOf="@id/round_up_switch"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintEnd_toEndOf="parent" />

5338cf87c61d15c9.png

新增小費計算結果

就快完成了版面配置!在這個步驟中,您會為小費的結果加上 TextView、將其放在「Calculate」按鈕下方,並且與結尾處對齊,而非與其他 UI 元素一樣放在開始處。

  1. 新增具有 ID 為 tip_result 且 ID 為 Tip AmountTextView
  2. TextView 的結尾邊緣限制在父項的終點邊緣。
  3. 限制「Calculate」按鈕的上方與下方的邊緣。
<TextView
    android:id="@+id/tip_result"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toBottomOf="@id/calculate_button"
    android:text="Tip Amount" />

9644bcdabbd8d7d1.png

  1. 執行應用程式。看起來應該要像這個螢幕截圖一樣。

e4ed552fa9fbe4ce.png

真厲害,如果您是第一次使用 XML 的話,那真是太棒了!

請注意,應用程式範本可能會與螢幕截圖有所不同,這是因為範本可能會在後續的 Android Studio 版本中有所變更。「Calculate」按鈕目前還沒有任何作用,但您可以輸入費用、選取小費百分比,然後切換是否要將小費四捨五入。在下一個程式碼研究室中您將會讓「Calculate」按鈕正常運作,所以請務必回來查看!

7. 採用好的程式設計做法

擷取字串

您可能會注意到有關硬式編碼字串的警告。請回顧先前的程式碼研究室,瞭解如何將字串擷取到資源檔案,以便更輕鬆地將應用程式翻譯成其他語言,並重複使用字串。瀏覽 activity_main.xml 並擷取所有字串資源。

  1. 按一下字串;將滑鼠游標懸停在顯示的黃色燈泡圖示上,然後按一下旁邊的三角形圖示;選擇「Extract String Resource」。字串資源的預設名稱沒有問題。您可以視需要根據提示選擇使用 amazing_servicegood_serviceok_service,讓名稱更加描述性。

現在請驗證剛新增的字串資源。

  1. 如果畫面上未顯示「Project」視窗,請按一下視窗左側的「Project」分頁標籤。
  2. 開啟 app > res > values > string.xml 查看所有 UI 字串資源。
<resources>
    <string name="app_name">Tip Time</string>
    <string name="cost_of_service">Cost of Service</string>
    <string name="how_was_the_service">How was the service?</string>
    <string name="amazing_service">Amazing (20%)</string>
    <string name="good_service">Good (18%)</string>
    <string name="ok_service">Okay (15%)</string>
    <string name="round_up_tip">Round up tip?</string>
    <string name="calculate">Calculate</string>
    <string name="tip_amount">Tip Amount</string>
</resources>

重新格式化 XML

Android Studio 提供各式各樣的程式碼,可用於調整程式碼,確保程式碼符合建議的程式設計慣例。

  1. activity_main.xml 中,選擇「Edit」>「Select All」
  2. 依序選擇「Code」>「Reformat Code」

以確保縮排一致,且可能會將 UI 元素的部分 XML 重新排序,例如將單一元素的所有 android: 屬性放在一起。

8. 解決方案程式碼

bcc5260318477c14.png

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:layout_width="160dp"
        android:layout_height="wrap_content"
        android:hint="@string/cost_of_service"
        android:inputType="numberDecimal"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/how_was_the_service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service" />

    <RadioGroup
        android:id="@+id/tip_options"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checkedButton="@id/option_twenty_percent"
        android:orientation="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/service_question">

        <RadioButton
            android:id="@+id/option_twenty_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/amazing_service" />

        <RadioButton
            android:id="@+id/option_eighteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/good_service" />

        <RadioButton
            android:id="@+id/option_fifteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/ok_service" />
    </RadioGroup>

    <Switch
        android:id="@+id/round_up_switch"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:checked="true"
        android:text="@string/round_up_tip"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@id/tip_options"
        app:layout_constraintTop_toBottomOf="@id/tip_options" />

    <Button
        android:id="@+id/calculate_button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/calculate"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/round_up_switch" />

    <TextView
        android:id="@+id/tip_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tip_amount"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/calculate_button" />
</androidx.constraintlayout.widget.ConstraintLayout>

res/values/strings.xml

<resources>
   <string name="app_name">Tip Time</string>
   <string name="cost_of_service">Cost of Service</string>
   <string name="how_was_the_service">How was the service?</string>
   <string name="amazing_service">Amazing (20%)</string>
   <string name="good_service">Good (18%)</string>
   <string name="ok_service">Okay (15%)</string>
   <string name="round_up_tip">Round up tip?</string>
   <string name="calculate">Calculate</string>
   <string name="tip_amount">Tip Amount</string>
</resources>

9. 摘要

  • XML (擴充標記語言) 可用來管理文字,由標記、元素和屬性構成。
  • 使用 XML 定義 Android 應用程式的版面配置。
  • 使用 EditText 可讓使用者輸入內容或編輯文字。
  • EditText 可提示用戶指定該欄位的內容。
  • 指定 android:inputType 屬性,以限制用戶可輸入 EditText 欄位的文字類型。
  • 列出包含 RadioButtons 的獨家選項清單 (使用 RadioGroup 分組)。
  • RadioGroup 可以是垂直或水平,而您也可以指定要最初選取的 RadioButton
  • 使用 Switch 可讓用戶在兩個選項之間切換。
  • 您不必使用個別的 TextView,就可以將標籤新增至 Switch
  • ConstraintLayout 中的每個子項都必須包含垂直和水平限制條件。
  • 使用「start」和「end」限制條件來處理由左至右 (LTR) 和右至左 (RTL) 的語言。
  • 限制屬性名稱會依照 layout_constraint<Source>_to<Target>Of 的格式提供。
  • 如要讓 View 盡可能適用於目標 ConstraintLayout,請將開頭和結尾限制為父項的開頭和結尾,然後將寬度設為 0dp。

10. 瞭解詳情

以下提供涵蓋主題的更多說明文件連結。如要查看所有 Android 開發說明文件,請前往 developer.android.com 別忘了,如果遇到困難,您可以執行 Google 搜尋。

11. 自行練習

進行下列操作:

  • 建立其他計算器應用程式 (例如烹飪的單位轉換器),將毫升單位換算成液體或奶油量、從克杯換算成杯杯或從杯杯開始,以此類推。您需要哪些欄位?