打造更優質的使用者體驗

1. 事前準備

如同先前程式碼研究室所述,Material 是 Google 打造的設計系統,內含相關指南、元件和工具,可支援使用者介面設計最佳做法。在本程式碼研究室中,您將更新小費計算機應用程式 (來自先前的程式碼研究室),藉此提供更優質的使用者體驗,如以下最終螢幕截圖所示。您也可以在其他情境中測試應用程式,確保使用者享有流暢的使用體驗。

5743ac5ee2493d7.png

必要條件

  • 熟悉常見的使用者介面小工具,例如 TextViewImageViewButtonEditTextRadioButtonRadioGroupSwitch
  • 熟悉 ConstraintLayout 及藉由設定限制條件來定位子項檢視畫面
  • 輕鬆修改 XML 版面配置
  • 瞭解點陣圖圖像與向量可繪項目之間的差異
  • 可設定佈景主題中的佈景主題屬性
  • 可在裝置上開啟深色佈景主題
  • 先前曾修改應用程式的 build.gradle 檔案以取得專案依附元件

課程內容

  • 如何在應用程式中使用 Material Design 元件
  • 如何從 Image Asset Studio 匯入 Material 圖示,使得可在應用程式中使用 Material 圖示
  • 如何建立及套用新樣式
  • 如何設定顏色以外的佈景主題屬性

建構項目

  • 遵循建議的使用者介面最佳做法打造優質小費計算機應用程式

需求條件

  • 已安裝 Android Studio 4.1 以上版本的電腦。
  • 完成先前的程式碼研究室而獲得的 Tip Time 應用程式的程式碼

2. 入門應用程式總覽

您在先前的程式碼研究室已建構 Tip Time 應用程式,這是一款小費計算機應用程式,且提供可自訂小費的選項。您的應用程式使用者介面看似像如下螢幕截圖。功能可以運作,但看起來更像原型。這些欄位在視覺上未不完全對齊。在更一致的樣式、間距和使用 Material Design 元件方面,有必要著手改善。

6685eaafba30960a.png

3. Material 元件

Material 元件是常見的使用者介面小工具,可協助您輕鬆在應用程式中導入 Material 樣式。這份說明文件說明如何使用及自訂 Material Design 元件。有每個元件適用的一般 Material Design 指南,而 Android 平台則提供適用於 Android 上可用元件的相關指南。已加標籤的圖表可提供足夠的資訊,協助您在所選平台上重新建立不存在的元件。

c4a4db857bb36c3f.png

使用 Material 元件,您的應用程式就能夠與使用者裝置上的其他應用程式搭配運作,使操作更一致。如此,在一個應用程式中學到的使用者介面模式可以延續到下一個應用程式。這樣使用者就能更快地瞭解如何使用您的應用程式。因此建議盡可能使用 Material 元件 (而不是非 Material 小工具)。此外,做法的運用也很有彈性且可依個人需求加以調整,如您將在下一工作中學到。

Material Design 元件 (MDC) 程式庫必須作為依附元件納入專案中。如果您正在使用 Android Studio 4.1 以上版本,依據預設,專案中應該已經包含此行。在應用程式的 build.gradle 檔案中,確認最新版程式庫包含這項依附元件。詳情請參閱 Material 網站上的 入門指南 頁面。

app/build.gradle

dependencies {
    ...
    implementation 'com.google.android.material:material:<version>'
}

文字欄位

在小費計算機應用程式中,版面配置頂端會顯示目前服務費的 EditText 欄位。此 EditText 欄位可正常運作,但文字欄位的外觀和行為不符合最新 Material Design 指南。

對於您想要使用的任何新元件,請先在 Material 網站瞭解相關資訊。關於文字欄位指南分為兩種文字欄位:

實心的文字欄位

Bea54a560820fe84.png

含外框的文字欄位

c37af7d70aad8aa6.png

若要建立上述文字欄位,請使用 MDC 程式庫中內含 TextInputEditTextTextInputLayout。可輕鬆自訂 Material 文字欄位以進行下列操作:

  • 顯示始終可見的文字或標籤
  • 在文字欄位中顯示圖示
  • 顯示小幫手或錯誤訊息

在本程式碼研究室的第一個工作中,您會將服務費 EditText 替換為 Material 文字欄位 (由 TextInputLayoutTextInputEditText 所組成)。

  1. 在 Android Studio 中開啟 Tip Time 應用程式,前往 activity_main.xml 版面配置檔案。其中應包含有小費計算機版面配置的 ConstraintLayout
  2. 若要查看 Material 文字欄位的 XML 樣貌,請返回 Android 指南的文字欄位。您應該會看到像這樣的程式碼片段:
<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/textField"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/label">

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    />

</com.google.android.material.textfield.TextInputLayout>
  1. 看到這個範例之後,請插入 Material 文字欄位作為 ConstraintLayout 的第一個子項 (在EditText欄位之前)。您將在後續步驟中移除 EditText 欄位。

你可以在 Android Studio 中輸入此內容,並使用自動完成功能來簡化輸入程序。您也可以從說明文件頁面複製範例 XML,然後貼到像這樣的版面配置中。請注意,TextInputLayout 含有子項檢視畫面 TextInputEditText。請注意,省略符號 (...) 主要用於表示縮寫的程式碼片段,因此,您可以專注在已實際變更的 XML 行。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    ...>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/label">

        <com.google.android.material.textfield.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

    </com.google.android.material.textfield.TextInputLayout>

    <EditText
        android:id="@+id/cost_of_service" ... />

    ...

您應該會看到 TextInputLayout 元素有錯誤。您尚未在父項 ConstraintLayout 中適當的限制此檢視畫面。系統也無法辨識字串資源。您會在後續步驟中修正這些錯誤。

1cf3f25cc047f291.png

  1. 在文字欄位中加入垂直和水平限制條件,以便正確放置於父項 ConstraintLayout 中。由於您尚未刪除 EditText,所以請從 EditText 剪下及貼上下列屬性並置放到 TextInputLayout 中:限制、資源 ID cost_of_service160dp 的版面配置寬度、wrap_content 的版面配置高度,以及提示文字 @string/cost_of_service
...

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/cost_of_service"
   android:layout_width="160dp"
   android:layout_height="wrap_content"
   android:hint="@string/cost_of_service"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toTopOf="parent">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"/>

</com.google.android.material.textfield.TextInputLayout>

...

您可能會看到錯誤訊息,指出 cost_of_service ID 與 EditText 的資源 ID 相同,但您目前可以忽略這項錯誤。(將用幾個步驟移除 EditText)。

  1. 接著確認 TextInputEditText 元素具有所有適當的屬性。將 EditText 的輸入類型剪下並貼上至 TextInputEditText.。將 TextInputEditText 元素資源 ID 變更為 cost_of_service_edit_text.
<com.google.android.material.textfield.TextInputLayout ... >

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/cost_of_service_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="numberDecimal" />

</com.google.android.material.textfield.TextInputLayout>

match_parent 的寬度和 wrap_content 的高度保持原樣。設定 match_parent 的寬度時,TextInputEditText 的寬度會與父項 TextInputLayout 相同,也就是 160dp

  1. 你現已從 EditText 複製所有相關資訊,請繼續刪除版面配置中的 EditText
  2. 在版面配置的「Design」(設計) 檢視畫面中,您應該會看到這個預覽畫面。服務費欄位現在看起來像是 Material 文字欄位。

148df54f0deda630.png

  1. 您目前無法執行這個應用程式,因為 calculateTip() 方法的 MainActivity.kt 檔案發生錯誤。請注意,先前的程式碼研究室已要求啟用專案的檢視畫面繫結,因此 Android 會根據資源 ID 名稱在繫結物件中建立屬性。我們從 XML 版面配置擷取的服務費欄位已經變更,因此 Kotlin 程式碼也必須隨之更新。

您現在將從資源 ID 為 cost_of_service_edit_textTextInputEditText 元素擷取使用者輸入內容。在 MainActivity 中,使用 binding.costOfServiceEditText 存取儲存於其中的文字字串。但 calculateTip() 方法的其餘部分可以維持不變。

private fun calculateTip() {
    // Get the decimal value from the cost of service text field
    val stringInTextField = binding.costOfServiceEditText.text.toString()
    val cost = stringInTextField.toDoubleOrNull()

    ...
}
  1. 真厲害!接著,執行應用程式並測試是否仍然正常運作。請注意,輸入時,「服務費」標籤顯示在你輸入的內容上方的方式。小費仍會照常計算。

b4a27e58f63417b7.png

切換按鈕

Material Design 指南中也提供切換按鈕的說明。切換按鈕是一種方便您開啟或關閉設定的小工具。

  1. 請參閱 Android 指南以瞭解 Material 的切換按鈕。您將會瞭解提供切換按鈕的 Material 樣式的 SwitchMaterial 小工具 (來自 MDC 程式庫)。如果您持續捲動瀏覽指南,就會看到 XML 範例。
  2. 若要使用 SwitchMaterial,您必須在版面配置中明確指定 SwitchMaterial,且使用完整的路徑名稱。

activity_main.xml 版面配置中,將 XML 標記從 Switch 變更為 com.google.android.material.switchmaterial.SwitchMaterial.

...

<com.google.android.material.switchmaterial.SwitchMaterial
    android:id="@+id/round_up_switch"
    android:layout_width="0dp"
    android:layout_height="wrap_content" ... />

...
  1. 執行應用程式,確認應用程式是否仍在編譯。應用程式沒有明顯異動。不過,使用 MDC 程式庫中的 SwitchMaterial (而非 Android 平台的 Switch) 有利於更新 SwitchMaterial 程式庫的實作 (例如 Material Design 指南的改變),然後您不需要取得變更,就能免費取得更新版小工具。這有助於應用程式永不過時。

這時您已經看到兩個範例,說明您的使用者介面如何使用現成的 Material Design 元件,以及如何讓您的應用程式更符合 Material 指南。請記得,你可以前往這個網站,探索 Android 提供的其他 Material Design 元件。

4. 圖示

圖示是一種符號,可透過視覺呈現的方式協助使用者瞭解使用者介面的預定功能,而且通常會以使用者預期在實體世界遇到的物體為靈感。圖示設計往往會將詳細資料精細程度降至供使用者熟悉所需的最低程度。舉例來說,實體世界中的鉛筆代表寫字,因此對應的圖示通常表示建立、新增或編輯項目。

在打開的筆記本上有更尖銳的尖鉛筆。相片來源:Angelina Litvin 發表於 Unsplash 網站上

黑白鉛筆圖示

有時候,圖示會與過時的實體世界遇到物體連結,如同磁碟片圖示一樣。此圖示普遍指示正在儲存檔案或資料庫記錄;不過,雖然磁碟片在 1970 年代很普及,但 2000 年以後就不再常見。然而,現今仍持續使用說明強烈的視覺效果如何超越其物理形式的生命週期。

磁碟片平放相片來源:Vincent Botta 發表於 Unsplash 網站上

磁碟片圖示

在應用程式中呈現圖示

針對應用程式中的圖示,建議您採用向量可繪項目,而不是針對不同螢幕密度提供不同版本的點陣圖圖像。向量可繪項目是以 XML 檔案表示,用於儲存如何建立圖像的操作說明,而非儲存組成該圖像的實際像素。向量可繪項目的可縮放,而不會損失視覺品質或增加檔案大小。

提供的圖示

Material Design 提供許多以常見類別排列的圖示,可配合您的需求。查看圖示清單

bfdb896506790c69.png

這些圖示也可以使用五種佈景主題 (實心、含外框、圓角、雙色和銳利) 中之一者繪製,並可使用顏色上色。

實心

含外框

圓角

雙色

銳利

新增圖示

在這項工作中,您將在應用程式中新增三個向量可繪項目的圖示:

  1. 服務費文字欄位旁邊的圖示
  2. 服務問題旁邊的圖示
  3. 「round up tip」(小費向上四捨五入) 提示旁邊的圖示

以下是應用程式最終版本的螢幕截圖。新增圖示後,您必須配合這些圖示的位置來調整版面配置。請注意,新增圖示後,欄位和計算按鈕會向右位移。

8c4225390dd1fb20.png

新增向量可繪項目素材資源

您可以直接使用 Android Studio 中的 Asset Studio 中建立這些圖示,作為向量可繪項目。

  1. 開啟應用程式視窗左側的「Resource Manager」(資源管理工具) 分頁標籤。
  2. 按一下 + 圖示,然後選取「Vector Asset」(向量素材資源)。

6a692157a2ada3f6.png

  1. 確認已選取「Asset Type」(素材資源類型) 中標示為「Clip Art」(插圖) 圓形按鈕。

698ab1c8dc2d1714.png

  1. 按一下「Clip Art:」(插圖) 旁的按鈕,選取其他插圖圖像。在隨即顯示的提示中,在顯示的視窗中輸入「"call made」(撥打電話)。您將使用這個箭頭圖示取得「round up tip」(小費向上四捨五入) 圖示。選取該項目並按一下「OK」(確定)。

50b0008ed6ab8d6d.png

  1. 將圖示重新命名為 ic_round_up。(在為圖示檔案命名時,建議使用前置字元 ic_)。將「**Size**」(大小) 保留為 24 dp x 24 dp,並將「**Color**」(顏色) 保留為黑色 000000。
  2. 按一下「下一步」
  3. 接受預設目錄位置,然後按一下「Finish」(完成)。

9f522a73be34ecf6.png

  1. 針對另外兩個圖示重複步驟 2 到 7:
  • 服務問題圖示:搜尋「room service」(聊天室服務) 圖示,然後將圖示儲存為 ic_service
  • 服務費圖示:搜尋「store」(商店) 圖示,並將其儲存為 ic_store
  1. 完成後,「Resource Manager」(資源管理工具) 會如以下螢幕截圖所示。此外,您的 res/drawable 資料夾也會列出三個向量可繪項目 (ic_round_upic_serviceic_store)。

3c895747fbfa3793.png

支援舊版 Android

您剛剛在應用程式中新增了向量可繪項目,但請特別注意,Android 5.0 (API 級別 21) 版本之前的 Android 平台不支援向量可繪項目。

根據專案設定方式,Tip Time 應用程式的最低 SDK 版本為 API 19。這表示該應用程式可以在執行 Android 平台 19 以上版本的 Android 裝置上執行。

若要讓您的應用程式在舊版 Android 裝置上運作 (稱為回溯相容性),請在應用程式的 build.gradle 檔案中新增 vectorDrawables 元素。這樣您就能使用比 API 21 版本舊的平台上使用向量可繪項目,而不需要在建構向量專案時轉換成 PNG。詳情請參閱此處

app/build.gradle

android {
  defaultConfig {
    ...
    vectorDrawables.useSupportLibrary = true
   }
   ...
}

專案設定完成之後,您就可以開始新增圖示到版面配置中。

插入圖示和位置元素

你必須使用 ImageViews 才能在應用程式中顯示圖示。這是最終顯示使用者介面的方式。

5d970eb04c642544.png

  1. 開啟 activity_main.xml 版面配置。
  2. 請先找出服務費文字欄位旁邊的商店圖示。在 TextInputLayout 之前插入新的 ImageView 作為 ConstraintLayout 的第一個子項。
<androidx.constraintlayout.widget.ConstraintLayout
   ...>

   <ImageView
       android:layout_width=""
       android:layout_height=""

   <com.google.android.material.textfield.TextInputLayout
       android:id="@+id/cost_of_service"
       ...
  1. ImageView 上設定適當的屬性來保留 ic_store 圖示。將 ID 設定為 icon_cost_of_service。將 app:srcCompat 屬性設定為可繪項目資源 @drawable/ic_store,且在 XML 行的旁邊顯示圖示的預覽。此外,由於圖像僅用於裝飾用途,所以設定 android:importantForAccessibility="no"
<ImageView
    android:id="@+id/icon_cost_of_service"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:importantForAccessibility="no"
    app:srcCompat="@drawable/ic_store" />

正常情況下,因為檢視畫面尚未受到限制,所以 ImageView 有錯誤。接下來,您將修復此問題。

  1. 以兩個步驟定位 icon_cost_of_service。首先新增對 ImageView 的限制條件 (此步驟),然後更新對旁邊的 TextInputLayout 的限制條件 (步驟 5)。下圖說明設定限制條件的方式。

e23287bdeca07a1e.png

ImageView 上,您想要將起始邊緣限制為父項檢視畫面 (app:layout_constraintStart_toStartOf="parent") 的起始邊緣。

該圖示與其旁邊的文字欄位相比垂直置中,因此應將此 ImageView 的頂端 (layout_constraintTop_toTopOf) 限制在文字欄位頂端。將此 ImageView 的底部 (layout_constraintBottom_toBottomOf) 限制為文字欄位的底部。如要參照文字欄位,請使用資源 ID @id/cost_of_service。預設行為是將兩個限制條件套用至相同維度 (例如頂端和底部限制條件) 的小工具時,系統會同樣套用限制條件。結果是圖示相對於服務費欄位垂直置中。

<ImageView
    android:id="@+id/icon_cost_of_service"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:importantForAccessibility="no"
    app:srcCompat="@drawable/ic_store"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/cost_of_service"
    app:layout_constraintBottom_toBottomOf="@id/cost_of_service" />

在「Design」(設計) 檢視畫面中,圖示和文字欄位仍會重疊。這將在下一個步驟中修正。

  1. 新增圖示前,文字欄位定位於父項的開頭,現在,必須移到右側。更新 cost_of_service 文字欄位中與 icon_cost_of_service 相關的限制條件。

40c0c8f04f53a87d.png

TextInputLayout 的起始邊緣應限制為 ImageView 的結束邊緣 (@id/icon_cost_of_service)。若要在兩個檢視畫面之間加入間距,請在 TextInputLayout 上加入 16dp 的起始邊界。

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/cost_of_service"
    ...
    android:layout_marginStart="16dp"
    app:layout_constraintStart_toEndOf="@id/icon_cost_of_service">

    <com.google.android.material.textfield.TextInputEditText ... />

</com.google.android.material.textfield.TextInputLayout>

完成上述所有變更後,圖示都必須正確放在文字欄位旁邊。

6ca04c3c964d5acc.png

  1. 接著在「服務品質如何?」旁邊插入服務鈴鐺圖示 TextView。雖然您可以在 ConstraintLayout 內的任何位置宣告 ImageView,但如果您在 XML 版面配置中在 TextInputLayout 之後、但在 service_question TextView 之前插入新的ImageView,您的 XML 版面配置會更易讀。

為新的 ImageView 指派 @+id/icon_service_question 資源 ID。設定關於 ImageView 和服務問題 TextView 的適當限制條件。

4487340b399e8105.png

此外,為 service_question TextView 加上 16dp 上邊界,讓服務問題與其上方的服務費欄位有更多垂直空間。

...

   <ImageView
        android:id="@+id/icon_service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        app:srcCompat="@drawable/ic_service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/service_question"
        app:layout_constraintBottom_toBottomOf="@id/service_question" />

    <TextView
        android:id="@+id/service_question"
        ...
        android:layout_marginTop="16dp"
        app:layout_constraintStart_toStartOf="@id/cost_of_service"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service"/>

...
  1. 此時,「Design」(設計) 檢視畫面看起來會像這樣。服務費欄位和服務問題 (及其各自的圖示) 看起來很清楚,但圓形按鈕的位置現在看起來不適當。而且不會與其上方的內容垂直對齊。

cdfd16c1851c88eb.png

  1. 將圓形按鈕移至服務問題下方的右側,可改善其位置。這表示更新 RadioGroup 限制條件。將 RadioGroup 的起始邊緣限制為 service_question TextView 的起始邊緣。RadioGroup 上的所有其他屬性都將維持不變。

58a62fae3d676fe4.png

...

<RadioGroup
    android:id="@+id/tip_options"
    ...
    app:layout_constraintStart_toStartOf="@id/service_question">

...
  1. 接下來,請將 ic_round_up 的圖示加到「Round up tip?」(小費向上四捨五入?) 切換按鈕旁邊的版面配置中。請嘗試自行完成此動作,如果遇到困難,請參考下方的 XML。你可以將新的 ImageView 資源 ID 指派給 icon_round_up
  2. 在版面配置 XML 中,在 RadioGroup 之後但在 SwitchMaterial 小工具之前插入新的 ImageView
  3. ImageView 資源 ID 指派給 icon_round_up,並將 srcCompat 設定為圖示 @drawable/ic_round_up 的可繪項目。將 ImageView 的開頭限制為父項的開頭,並也使圖示相對於 SwitchMaterial 垂直置中。
  4. SwitchMaterial 更新為在圖示旁邊,並有 16dp 起始邊界。對於 icon_round_upround_up_switch 所產生的 XML 看起來會像這樣。
...

   <ImageView
        android:id="@+id/icon_round_up"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        app:srcCompat="@drawable/ic_round_up"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/round_up_switch"
        app:layout_constraintBottom_toBottomOf="@id/round_up_switch" />

    <com.google.android.material.switchmaterial.SwitchMaterial
        android:id="@+id/round_up_switch"
        ...
        android:layout_marginStart="16dp"
        app:layout_constraintStart_toEndOf="@id/icon_round_up" />

...
  1. 「Design」(設計) 檢視畫面看起來會像這樣。這三個圖示均已正確定位。

f4b0e8d61d91986b.png

  1. 如果比較此畫面與最終的應用程式螢幕截圖,就會發現計算按鈕也會位移以與服務費欄位、服務問題、圓形按鈕選項和小費向上四捨五入問題垂直對齊。只要將計算按鈕的起點限制為 round_up_switch 的開頭,就能達到此效果。也會在計算按鈕與其上方的切換按鈕之間會加入 8dp 垂直邊界。

60d5a43819c2367d.png

...

<Button
   android:id="@+id/calculate_button"
   ...
   android:layout_marginTop="8dp"
   app:layout_constraintStart_toStartOf="@id/round_up_switch" />

...
  1. 最後但同樣重要的,將 8dp 上邊界加至 TextView 就可定位 tip_result

Beb333a518b51323.png

...

<TextView
   android:id="@+id/tip_result"
   ...
   android:layout_marginTop="8dp" />

...
  1. 步驟很多!建議你逐步完成這些步驟。需要非常注重細節才能使元素在版面配置中正確對齊,使最終結果看起來更好!執行應用程式,畫面應該看起來像下方的螢幕截圖。使元素垂直對齊並增加元素之間的間距,這些元素就不會擁擠。

1f2ef2c0c9a9bdc7.png

程序尚未完成!你可能已注意到服務問題及小費金額的字型大小和顏色看起來與圓形按鈕及切換按鈕中的文字不同。在下一個工作中,請使用樣式和佈景主題來使這些元素保持一致。

5. 樣式與佈景主題

樣式是單一類型小工具的檢視畫面屬性值集合。舉例來說,TextView 樣式可指定字型顏色、字型大小和背景顏色等。只要將這些屬性擷取成樣式,就能輕鬆地將樣式套用至版面配置的多個檢視畫面,且維持在單一位置。

在這項工作中,您必須先建立文字檢視畫面、圓形按鈕和切換按鈕小工具的樣式。

建立樣式

  1. 如果在「res > values」目錄沒有名為 styles.xml 的新檔案,請建立該檔案。用滑鼠右鍵按一下「values」(值) 目錄,然後選取「New」(新增) >「Values Resource File」(值資源檔案)。呼叫 styles.xml。新檔案包含下列內容。
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
  1. 建立新的 TextView 樣式,使文字在整個應用程式中保持一致。在 styles.xml 中定義樣式一次,然後將該樣式套用至版面配置中的所有 TextViews。雖然您可以從頭開始定義樣式,但您可以從 MDC 程式庫中現有的 TextView 樣式延伸。

設定元件樣式時,一般而言,您應從所用小工具類型的父項樣式中延伸。這麼做有兩個非常重要的理由:第一,確認已設定元件的所有重要預設值;第二,您的樣式會繼續沿用日後的父項樣式變更。

您可以視需要為樣式命名,但我們建議採用慣例。如果您沿用父項 Material 樣式,請將 MaterialComponents 替換成您的應用程式名稱 (TipTime),以平行方式命名樣式。這麼做會將您的變更移到各自的命名空間中,進而在 Material Components 推出新樣式時避免發生衝突。範例:

您的樣式名稱:Widget.TipTime.TextView 沿用父項樣式:Widget.MaterialComponents.TextView

請此內容加至 styles.xml 檔案的 resources 開頭與結尾標記之間。

<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
</style>
  1. 設定 TextView 樣式,使其會覆寫屬性 android:minHeight,android:gravity,android:textAppearance.

android:minHeight 設定 TextView 上的高度下限為 48dp。根據 Material Design 指南,任一資料列的最小高度應為 48 dp。

設定 android:gravity 屬性就可將 TextView 中的文字垂直置中。(請參閱下方的螢幕截圖。)重力可控制檢視畫面中內容本身的定位方式。由於實際文字內容的高度不會佔據整個 48dp,所以 center_vertical 值會將 TextView 內的文字垂直置中 (但不變更水平位置)。其他可能的重力值包括 centercenter_horizontaltopbottom。歡迎嘗試其他重力值,來瞭解對文字的影響。

bd89f5a76d67ada6.png

將文字外觀屬性值設定為 ?attr/textAppearanceBody1。TextAppearance 是一組預先製作的樣式,用於文字大小、字型和其他文字屬性。若要進一步瞭解 Material 可能提供的其他文字外觀,請參閱型別小數位數 (type scale) 清單

<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
    <item name="android:minHeight">48dp</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
  1. activity_main.xml 的每 TextView 上新增樣式屬性,即可將 Widget.TipTime.TextView 樣式套用到 service_question TextView
<TextView
    android:id="@+id/service_question"
    style="@style/Widget.TipTime.TextView"
    ... />

在此樣式之前,TextView 的字型看起來很小,及字型顏色是灰色:

5cd99583da77efba.png

新增樣式之後,TextView 看起來像這樣。現在,此 TextView 的樣式看起來與版面配置的其餘部分更一致。

296a89a6015d9e15.png

  1. 將相同的 Widget.TipTime.TextView 樣式套用到 tip_result TextView
<TextView
    android:id="@+id/tip_result"
    style="@style/Widget.TipTime.TextView"
    ... />

c45860bda6761be7.png

  1. 相同的文字樣式應套用至切換按鈕中的文字標籤。不過,您無法將 SwitchMaterial 小工具設定為 TextView 樣式。TextView 樣式只能套用到 TextViews。因此需要建立用於切換按鈕的新樣式。屬性在 minHeightgravitytextAppearance 方面皆相同。此處的樣式名稱與父項不同,因為您現在正在沿用 MDC 程式庫的 Switch 樣式。樣式的名稱也應與父項樣式的名稱雙向同步。

您的樣式名稱:Widget.TipTime.CompoundButton.Switch。沿用父項樣式:Widget.MaterialComponents.CompoundButton.Switch

<style name="Widget.TipTime.CompoundButton.Switch" parent="Widget.MaterialComponents.CompoundButton.Switch">
   <item name="android:minHeight">48dp</item>
   <item name="android:gravity">center_vertical</item>
   <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>

您也可以指定此樣式中切換按鈕特有的其他屬性,但不必在你的應用程式中完成這項動作。

  1. 圓形按鈕文字是想要確定文字視覺上保持一致的最後一項。您無法將 TextView 樣式或 Switch 樣式套用至 RadioButton 小工具。而是,您必須建立用於圓形按鈕的新樣式。您可以從 MDC 程式庫的 RadioButton 樣式延伸。

建立此樣式時,也在圓形按鈕文字與圓形影像內容之間加入邊框間距。paddingStart 是您尚未使用的新屬性。邊框間距是指檢視畫面內容與檢視畫面邊界之間的空間量。paddingStart 屬性只會在元件的開頭設定邊框間距。請查看圓形按鈕上 paddingStart 的 0dp 與 8dp 的差異。

e1cef41d95740600.png

25f75f5c36085e76.png

<style name="Widget.TipTime.CompoundButton.RadioButton"
parent="Widget.MaterialComponents.CompoundButton.RadioButton">
   <item name="android:paddingStart">8dp</item>
   <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
  1. (選用) 建立 dimens.xml 檔案以改善常用值的可管理性。您可以與上述 styles.xml 檔案相同方式建立檔案。選取「values」(值) 目錄,然後用滑鼠右鍵按一下並選取「New」(新增) >「Values Resource File」(值資源檔案)。

在這個小型應用程式中,你重複執行最小高度設定兩次。目前當然可以管理,但要是有 4 個、6 個、10 個或更多個元件共用相同的值,就會迅速失控。記得逐一變更每個項目是很繁瑣又容易出錯。您可以在 res > values 中建議另一個名為 dimens.xml 的實用資源檔案,保留您可命名的通用維度。將通用值標準化為已命名維度,就能更輕鬆地管理應用程式。TipTime 很短,因此我們不會在此選用步驟外使用此程式碼。不過,如果您在可能會與設計團隊合作的實際工作環境中擁有較複雜的應用程式,則 dimens.xml 可讓您輕鬆變更這些值。

dimens.xml

<resources>
   <dimen name="min_text_height">48dp</dimen>
</resources>

更新 styles.xml 檔案即可直接使用 @dimen/min_text_height,而不是 48dp

...
<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
    <item name="android:minHeight">@dimen/min_text_height</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
...

將這些樣式加入您的佈景主題

您可能已經注意到,您尚未將新的 RadioButtonSwitch 樣式套用至各別的小工具。這是因為您需要使用佈景主題屬性來設定應用程式佈景主題中的 radioButtonStyleswitchStyle。讓我們回想一下佈景主題。

佈景主題是一組具名資源 (名為佈景主題屬性),方便之後在樣式、版面配置等中參照。您可以指定整個應用程式、活動或檢視區塊階層的佈景主題,而不只是指定個別 View.。你先前在 themes.xml 中設定了 colorPrimarycolorSecondary 等佈景主題屬性,藉此修改應用程式的佈景主題,然後在應用程式和其元件中使用這些佈景主題。

radioButtonStyleswitchStyle 是可以設定的其他佈景主題屬性。您為這些佈景主題屬性提供的樣式資源會套用至佈景主題所套用的檢視區塊階層中的每個圓形按鈕和每個切換按鈕。

還有用於 textInputStyle 的佈景主題屬性,系統會將指定的樣式資源套用到應用程式中的所有文字輸入欄位。若要TextInputLayout看起來像有外框的文字欄位 (如 Material Design 指南所示),MDC 程式庫中有定義為 Widget.MaterialComponents.TextInputLayout.OutlinedBoxOutlinedBox 的樣式。您將使用的樣式。

b00a91da56e6f6e2.png

  1. 修改 themes.xml 檔案,讓佈景主題參照您想要的樣式。設定佈景主題屬性的方式與先前在程式碼研究室中宣告 colorPrimarycolorSecondary 佈景主題屬性的方式相同。不過,相關的佈景主題屬性為 textInputStyleradioButtonStyleswitchStyle。您會使用先前為 RadioButtonSwitch 建立的樣式,以及 Material OutlinedBox 的文字欄位的樣式。

將下列內容複製到 res/values/themes.xml 而複製到應用程式佈景主題的樣式標記。

<item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
<item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
<item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
  1. 您的 res/values/themes.xml 檔案看起來會像這樣。您可以視需要在 XML 中加入註解 (以 <!---> 指示)。
<resources xmlns:tools="http://schemas.android.com/tools">

    <!-- Base application theme. -->
    <style name="Theme.TipTime" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        ...
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Text input fields -->
        <item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
        <!-- Radio buttons -->
        <item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
        <!-- Switches -->
        <item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
    </style>

</resources>
  1. 請務必對themes.xml (night) 中的深色佈景主題進行相同變更。您的 res/values-night/themes.xml 檔案看起來會像這樣。
<resources xmlns:tools="http://schemas.android.com/tools">

    <!-- Application theme for dark theme. -->
    <style name="Theme.TipTime" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        ...
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Text input fields -->
        <item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
        <!-- For radio buttons -->
        <item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
        <!-- For switches -->
        <item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
    </style>

</resources>
  1. 執行應用程式並查看變更。文字欄位的 OutlinedBox 樣式看起來更好,所有文字現在看起來都一致!

31ac15991713b031.png 3e861407146c9ed4.png

6. 加強使用者體驗

隨著您即將完成應用程式,除了測試預期的工作流程外,還要在其他使用者情境中測試應用程式。您可能會發現小幅變更程式碼可以大幅改善使用者體驗。

旋轉裝置

  1. 將裝置旋轉為水平模式。您可能需要先啟用「Auto-rotate」(自動旋轉) 設定。(位於裝置的「Quick Settings」(快速設定) 下方,或位於「Settings」(設定) >「Display」(顯示器) >「Advanced」(進階) >「Auto-rotate screen」(自動旋轉螢幕) 選項下方)。

f2edb1ae9926d5f1.png

接著,在模擬器中,您可以使用模擬器選項 (位於相鄰於裝置的右上角) 將畫面向右或向左旋轉。

da8aee11166adf41.png

  1. 您會發現部分使用者介面元件 (包括「Calculate」(計算) 按鈕會遭到截斷。這顯然使您無法使用應用程式!

d73499f9c9d2b330.png

  1. 若要解決這個問題,請在 ConstraintLayout 四周加入 ScrollView。您的 XML 看起來有些像這樣。
<ScrollView
   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_height="match_parent"
   android:layout_width="match_parent">

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

       ...
   </ConstraintLayout>

</ScrollView>
  1. 再次執行並測試應用程式。將裝置旋轉為橫向模式時,您應能夠捲動使用者介面來存取計算按鈕,並查看小費結果。此修正功能不僅可用於橫向模式,而且也適用於其他不同尺寸的 Android 裝置。現在無論裝置螢幕大小為何,使用者都可以捲動瀏覽版面配置。

按下 Enter 鍵隱藏鍵盤

你可能已注意到,輸入服務費後,鍵盤仍會保持顯示狀態。每次存取「calculate」(計算) 按鈕都需要手動隱藏鍵盤會有點麻煩。改為按下 Enter 鍵時,讓鍵盤自動隱藏。

e2c3a3dbc40218a2.png

您可以定義文字欄位的鍵事件監聽器,使得可回應於使用者輕觸特定按鍵的事件。鍵盤上每個可能的輸入項目選項都有相關聯的按鍵碼,包括 Enter 鍵。請注意,螢幕小鍵盤 (又稱為螢幕鍵盤) 與實體鍵盤不同。

1c95d7406d3847fe.png

在這項工作中,在文字欄位中設定按鍵事件監聽器,以監聽何時按下 Enter 鍵。偵測到事件時隱藏鍵盤。

  1. 複製這個 Helper 方法並貼到 MainActivity 類別中,您可以插入在 MainActivity 類別的大括號之前。handleKeyEvent() 是一種私人輔助函式,可以在 keyCode 輸入參數等於 KeyEvent.KEYCODE_ENTER 時隱藏螢幕小鍵盤。InputMethodManager 可控制是否要顯示螢幕鍵盤、隱藏螢幕鍵盤,及讓使用者能夠自行選擇要顯示的螢幕鍵盤。如果系統處理了按鍵事件,則此方法會傳回 true,否則傳回 false。

MainActivity.kt

private fun handleKeyEvent(view: View, keyCode: Int): Boolean {
   if (keyCode == KeyEvent.KEYCODE_ENTER) {
       // Hide the keyboard
       val inputMethodManager =
           getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
       inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
       return true
   }
   return false
}
  1. 現在,在 TextInputEditText 小工具上附加一個按鍵事件監聽器。請記住,您可以透過 binding.costOfServiceEditText. 繫結物件存取 TextInputEditText 小工具

costOfServiceEditText 上呼叫 setOnKeyListener() 方法,然後傳入 OnKeyListener。做法與在應用程式中使用 binding.calculateButton.setOnClickListener { calculateTip() }. 設定「calculate」(計算) 按鈕的點擊事件監聽器的方式類似

在檢視畫面上設定按鍵事件監聽器的程式碼較為複雜,但一般而言,OnKeyListener 具有在發生按下按鍵時觸發的 onKey() 方法。onKey() 方法採用 3 個輸入引數:檢視畫面、已按下按鍵的程式碼,及按鍵事件 (您不會使用,因此您可以呼叫「_」)。呼叫 onKey() 方法時,應呼叫 handleKeyEvent() 方法,並傳遞檢視畫面和按鍵碼引數。編寫此內容的語法是:view, keyCode, _ -> handleKeyEvent(view, keyCode). 這實際上呼叫 lambda 運算式,但在後續單元中您將更瞭解 lambda。

加入用於設定活動的 onCreate() 方法中文字欄位的按鍵事件監聽器的程式碼。這是因為您希望在建立版面配置後,以及使用者開始與活動互動前,立即附加按鍵事件監聽器。

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
   ...

   setContentView(binding.root)

   binding.calculateButton.setOnClickListener { calculateTip() }

   binding.costOfServiceEditText.setOnKeyListener { view, keyCode, _ -> handleKeyEvent(view, keyCode)
   }
}
  1. 測試新的變更是否正常運作。執行應用程式並輸入服務費。按下鍵盤上的 Enter 鍵就會隱藏螢幕鍵盤。

測試已啟用 TalkBack 的應用程式

在完成本課程後,您想要建構的應用程式可供盡可能更多使用者存取。部分使用者可能會使用 Talkback 來存取及瀏覽您的應用程式。TalkBack 是 Android 裝置隨附的 Google 螢幕閱讀器。TalkBack 的互動朗讀功能讓您不看螢幕也能輕鬆使用裝置。

啟用 TalkBack 後,請確保使用者能夠在應用程式中完成計算小費的用途。

  1. 按照這些操作說明在裝置上啟用 TalkBack。
  2. 返回 Tip Time 應用程式。
  3. 請參閱操作說明,使用 Talkback 探索應用程式。向右滑動即可逐一瀏覽螢幕元素,向左滑動後即可往反方向瀏覽。輕觸兩下任一處即可選取。確認您可以透過滑動手勢找到應用程式中的所有元素。
  4. 確認 Talkback 使用者可以瀏覽畫面上的每個項目、輸入服務費、變更小費選項、計算小費,以及聽取公告的小費。請記住,不會提供標記為 importantForAccessibility="no" 的圖示的互動朗讀。

若要進一步瞭解如何讓您的應用程式更容易使用,請參閱這些原則學習課程

(選用) 調整向量可繪項目的色調

在此項選用工作中,您可以依據佈景主題的主要顏色將圖示上色,讓淺色和深色佈景主題中的圖示看起來不同 (如下所示)。這項變更是為了強化使用者介面的色彩,使圖示與應用程式佈景主題更融合。

77092f702beb1cfb.png 80a390087905eb29.png

如先前所述,與點陣圖圖像相比,VectorDrawables 的優點之一就是能夠縮放圖像及上色。下方的 XML 代表鈴鐺圖示。請留意,有兩個顏色屬性:android:tintandroid:fillColor

ic_service.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:width="24dp"
   android:height="24dp"
   android:viewportWidth="24"
   android:viewportHeight="24"
   android:tint="?attr/colorControlNormal">
 <path
     android:fillColor="@android:color/white"
     android:pathData="M2,17h20v2L2,19zM13.84,7.79c0.1,-0.24 0.16,-0.51 0.16,-0.79 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2c0,0.28 0.06,0.55 0.16,0.79C6.25,8.6 3.27,11.93 3,16h18c-0.27,-4.07 -3.25,-7.4 -7.16,-8.21z"/>
</vector>

bdddc76d0ca06573.png

如果有色調,則會覆寫可繪項目的任何 fillColor 指令。在這種情況下,系統會用 colorControlNormal 佈景主題屬性覆寫白色顏色。colorControlNormal 是小工具的「正常」(未選取/未啟用狀態) 的顏色。目前是灰色。

我們在應用程式中可設計的視覺強化項目之一,就是根據應用程式佈景主題的主要顏色來將可繪項目上色。針對淺色佈景主題,圖示會顯示為 @color/green,而在深色佈景主題中,圖示會顯示為 @color/green_light,也就是 ?attr/colorPrimary。根據應用程式佈景主題的主要顏色來將可繪項目上色,可以讓版面配置中的元素看起來更一致且融合。這樣也不必複製淺色佈景主題和深色佈景主題的各組圖示。只有 1 組向量可繪項目,且色調會根據 colorPrimary 佈景主題屬性而改變。

  1. 變更 ic_service.xmlandroid:tint 屬性的值
android:tint="?attr/colorPrimary"

在 Android Studio 中,圖示現在有適當的色調。

f0b8f59dbf00a20b.png

colorPrimary 佈景主題屬性指向的值會因淺色和深色佈景主題而有所不同。

  1. 重複上述步驟,變更其他向量可繪項目的色調。

ic_store.xml

<vector ...
   android:tint="?attr/colorPrimary">
   ...
</vector>

ic_round_up.xml

<vector ...
   android:tint="?attr/colorPrimary">
   ...
</vector>
  1. 執行應用程式。確認圖示在淺色和深色佈景主題中的顯示方式不同。
  2. 作為最後的清理步驟,請記得重新設定應用程式中所有 XML 和 Kotlin 程式碼檔案的格式。

恭喜,您已完成小費計算機應用程式!您應該對於自己建構的作品感到自豪。希望這能為你打造更精美、功能更齊全的應用程式!

7. 解決方案程式碼

本程式碼研究室的解決方案程式碼位於下列 GitHub 存放區中。

5743ac5ee2493d7.png ab4acfeed8390465.png

若要取得本程式碼研究室的程式碼,並在 Android Studio 中開啟,請按照下列步驟:

取得程式碼

  1. 按一下所提供的網址。這會在瀏覽器中開啟專案的 GitHub 頁面。
  2. 在專案的 GitHub 頁面中,按一下「Code」(程式碼) 按鈕就會開啟對話方塊。

5b0a76c50478a73f.png

  1. 在對話方塊中點選「Download ZIP」(下載 ZIP) 按鈕,將專案儲存至電腦。等待下載完成。
  2. 在電腦上尋找檔案 (可能位於「下載」資料夾中)。
  3. 按兩下 ZIP 檔案,將檔案解壓縮。這項操作會建立含有專案檔案的新資料夾。

在 Android Studio 中開啟專案

  1. 啟動 Android Studio。
  2. 在「Welcome to Android Studio」(歡迎使用 Android Studio) 視窗中,按一下「Open an existing Android Studio project」(開啟現有的 Android Studio 專案)。

36cc44fcf0f89a1d.png

注意:如果 Android Studio 已開啟,請依序選取「File」(檔案) >「New」(新增) >「Import Project」(匯入專案) 選單選項。

21f3eec988dcfbe9.png

  1. 在「Import Project」(匯入專案) 對話方塊中,前往未壓縮專案資料夾所在的位置 (可能位於「下載」資料夾中)。
  2. 按兩下該專案資料夾。
  3. 等待 Android Studio 開啟專案。
  4. 按一下「Run」(執行) 按鈕 11c34fc5e516fb1c.png 即可建構並執行應用程式,請確認應用程式的建構符合預期。
  5. 在「Project」(專案) 工具視窗中瀏覽專案檔案,查看應用程式的設定方式。

8. 摘要

  • 請盡可能採用符合 Material Design 指南的 Material Design 元件,並提供更多自訂功能。
  • 新增圖示以提供可視提示,讓使用者瞭解應用程式的哪些功能如何運作。
  • 使用 ConstraintLayout 將元素放在版面配置中。
  • 針對邊緣情況測試應用程式(例如,在橫向模式下旋轉應用程式),並在適用的情況下進行改進。
  • 請撰寫程式碼的註解,協助閱讀程式碼的其他人瞭解您的做法。
  • 重新設定程式碼格式並清理程式碼,以盡可能精簡。

9. 瞭解詳情

10. 自行練習

  • 接續先前的程式碼研究室中,使用您在此處學到的最佳做法來更新烹飪材料單位換算器應用程式,使得更符合 Material 指南。