新增圖片至 Dice Roller 應用程式

1. 事前準備

在本程式碼研究室中,您將新增骰子的圖片至現有的 Dice Roller Android 應用程式。請務必先建構 Dice Roller 應用程式的基礎,完成之前的程式碼研究室。

應用程式不會在 TextView 中顯示擲骰子的值,而是骰子擲出點數面的正確圖片。所以應用程式的影像內容會更豐富,並提供更好的使用者體驗。

c7f0d42525da7431.png

系統會提供下載骰子圖片的連結,讓您在應用程式中將這些圖片新增為資源。如要編寫要使用哪一張骰子圖片的程式碼,請在 Kotlin 中使用 when 陳述式。

必要條件

  • 已完成「建立互動式 Dice Roller 應用程式」程式碼研究室。
  • 可寫入控制流程的陳述式 (if / elsewhen 陳述式)。
  • 根據使用者輸入內容 (修改 MainActivity.kt 檔案) 即可更新應用程式的使用者介面。
  • 可新增點擊事件監聽器至 Button.
  • 可新增圖片資源至 Android 應用程式

課程內容

  • 如何在應用程式執行時更新 ImageView
  • 如何根據不同條件自訂應用程式行為 (使用 when 陳述式)。

建構項目

  • Dice Roller Android 應用程式,含有擲骰子及更新畫面圖片的 Button

需求條件

  • 安裝 Android Studio 的電腦。
  • 下載骰子圖片需要網路連線。

2. 更新應用程式的版面配置

在這項工作中,您要以顯示擲骰子結果圖片的 ImageView,取代版面配置中的 TextView

開啟 Dice Roller 應用程式

  1. 在 Android Studio 中,開啟並執行先前程式碼研究室的 Dice Roller 應用程式。您可以使用解決方案程式碼,或您建立的程式碼。

應用程式看起來如下。

2e8416293e597725.png

  1. 開啟 activity_main.xml (「app」(應用程式) >「res」(解析度) >「layout」(版面配置) >「activity_main.xml」)。系統隨即會開啟「Layout Editor」(版面配置編輯器)

刪除 TextView

  1. 在「Layout Editor」的「Component Tree」中選取 TextView

a6fc189dac34ee71.png

  1. 按一下滑鼠右鍵,選擇「Delete」或按下 Delete 鍵。
  2. 暫時忽略 Button 上的警告。您將在後續步驟中修正這個問題。

新增 ImageView 至版面配置

  1. ImageView 從「Palette」(區塊面板) 拖曳至「Design」(設計) 檢視畫面,並放置在 Button 上方。

91f6e2be0a01fbf.png

  1. 在「Pick a Resource」對話方塊中,選取「Sample data」下的「avatars」。這是您在下一項工作中新增骰子圖片前,暫時使用的圖片。

824493e9927da401.png

  1. 按一下「OK」。應用程式的「Design」檢視畫面應如下所示。

f9d5ee87018baee.png

  1. 在「Component Tree」中,您會看到兩項錯誤。Button 不受垂直限制,ImageView 則不受垂直或水平限制。

b8c3b83124c31ff.png

Button 不受垂直限制,因為您已移除原本置於下方的 TextView。接著,請在下方放置 ImageViewButton

調整 ImageView 和 Button 的位置

無論 Button 在什麼位置,ImageView 都必須垂直置於畫面中間。

  1. ImageView 中加入水平限制條件。將 ImageView 的左側連結至父項 ConstraintLayout 的左側邊緣。
  2. 連結 ImageView 的右側與父項的右側邊緣。這會讓父項中的 ImageView 水平置中。

9848bb6319e11777.png

  1. ImageView 加入垂直限制,然後連結 ImageView 的頂端與父項的頂端。ImageView 會向上滑動至 ConstraintLayout 的頂端。

2d8d134e6292d48f.png

  1. Button 中加入垂直限制,然後連結 Button 的頂端與 ImageView 底部。Button 會向上滑動至 ImageView 下方。

b6d3dcee6c7a51fc.png

  1. 再次選取 ImageView 並加入垂直限制,然後連結 ImageView 的底部與父項的底部。這會讓 ImageViewConstraintLayout 中垂直置中。

現在所有限制相關警告應已消失。

完成所有操作後,「Design」(設計) 檢視畫面應該如下所示,ImageView 位於中央,Button 則緊靠在下方。

1b05a6d2fd56459f.png

在「Component Tree」中,您可能會看到 ImageView 的警示,指出應在 ImageView 加入內容說明。目前不必擔心這項警示,因為之後在程式碼研究室中,您可以根據顯示的骰子圖片,設定 ImageView 的內容說明。這項變更會透過 Kotlin 程式碼完成。

3. 新增骰子圖片

在這項工作中,您必須下載一些骰子圖片,並新增至應用程式。

下載骰子圖片

  1. 開啟這個網址下載骰子圖片的 ZIP 檔案至電腦上。等待下載作業完成。
  2. 在電腦上尋找該檔案 (可能位於「下載」資料夾中)。
  3. 按兩下 ZIP 檔案,解壓縮檔案。這項操作會建立新的 dice_images 資料夾,內含顯示骰子點數 1 到 6 的 6 個骰子圖片檔案。

43c95351759ada02.png

新增骰子圖片至應用程式

  1. 在 Android Studio 中,依序按一下選單中的「View」>「Tool Windows」>「Resource Manager」,或點選「Project」視窗左側的「Resource Manager」分頁標籤。
  2. 按一下「Resource Manager」下方的「+」圖示,然後選取「Import Drawables」,即可開啟檔案瀏覽器。

67186ea5d631bc8a.png

  1. 找出並選取這 6 個骰子圖片檔案。您可以選取第一個檔案,然後按住 Shift 鍵並選取其他檔案。
  2. 按一下「Open」
  3. 依序點選「Next」和「Import」,確認要匯入這 6 項資源。

a45dff94a19e2722.png

a7ad66d623ac73c2.png

  1. 如果成功匯入檔案,應用程式的「Resource Manager」(資源管理員) (「app」(應用程式) >「res」(解析度) >「drawable」(可繪項目)) 中會顯示 6 張圖片。

ab68f82b385fc83e.png

做得好!在下一個工作中,您將在應用程式中使用這些圖片。

重要! - 在 Kotlin 程式碼中,您可以使用圖片的識別碼參考圖片:

  • R.drawable.dice_1
  • R.drawable.dice_2
  • R.drawable.dice_3
  • R.drawable.dice_4
  • R.drawable.dice_5
  • R.drawable.dice_6

4. 使用骰子圖片

更換範例顯示圖片

  1. 在「Design Editor」中,選取 ImageView
  2. 在「Declared Attributes」部分的「Attributes」中,找到已設為該顯示圖片工具「srcCompat」屬性

提醒您,工具「srcCompat」屬性只會在 Android Studio 的「Design」檢視畫面中使用提供的圖片。這張圖片只會在建構應用程式時向開發人員顯示,在模擬器或裝置上執行應用程式時不會顯示。

  1. 按一下顯示圖片的縮圖預覽。系統會開啟對話方塊,請選擇 ImageView 使用的新資源。

d8a26941179b3bdf.png

  1. 選取 dice_1 可繪項目,然後按一下「OK」

哇!ImageView 占滿整面螢幕。

1072e9fdd637afd9.png

接著,您可以調整 ImageView 的寬度和高度,避免遮掩 Button

  1. 在「Constraints Widget」下方的「Attributes」視窗中,找出「layout_width」和「layout_height」屬性。這兩項屬性目前設為「wrap_content」,即 ImageView 的高度和寬度與其中的內容 (來源圖片) 相同。
  2. 請將 ImageView 的寬度改設為固定的 160 dp,並將高度設為固定的 200 dp。按下 Enter 鍵。

ImageView 變小了。

9579582d8775e688.png

您可能發現 Button 太靠近圖片。

  1. 在「限制小工具」中,設定上邊界到下邊界並加入 16dp。

8c647d6ae28ef3a6.png

「Design」檢視畫面更新後,應用程式看來美觀多了!

b53f7379bfba8c27.png

按下按鈕時變更骰子圖片

我們已修正版面配置,但還必須更新 MainActivity 類別才能使用骰子圖片。

應用程式中的 MainActivity.kt 檔案目前有一項錯誤。如果您嘗試執行應用程式,就會看到以下建構錯誤:

aecce207cb5fc7.png

因為程式碼仍然參考從版面配置刪除的 TextView

  1. 開啟 MainActivity.kt (「應用程式」>「java」>「com.example.diceroller」>「MainActivity.kt」)

程式碼參考 R.id.textView,但 Android Studio 無法辨識。

3a923aa53fc3ba8a.png

  1. rollDice() 方法中,選取並刪除參考 TextView 的程式碼。
// Update the TextView with the dice roll
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = dice.roll().toString()
  1. 繼續留在 rollDice() 中,建立名為類型 ImageViewdiceImage 的新變數。從版面配置將它設為與 ImageView 等化。使用 findViewById() 方法,傳入 ImageViewR.id.imageView 的資源識別碼作為輸入引數。
val diceImage: ImageView = findViewById(R.id.imageView)

如果想知道如何找出 ImageView 的精確資源 ID,請查看「Attributes」視窗頂端的「ID」

cbfc9d5e01a04e32.png

在 Kotlin 程式碼中參考這個資源 ID 時,請務必輸入完全相同的內容 (小寫的 i、大寫的 V 且不含空格),否則 Android Studio 會顯示錯誤訊息。

  1. 新增這行程式碼,測試點擊按鈕時,可否正確更新 ImageView。擲骰子的點數不一定是「2」,只是使用 dice_2 圖片進行測試。
diceImage.setImageResource(R.drawable.dice_2)

以上程式碼會對 ImageView 呼叫 setImageResource() 方法,並傳遞 dice_2 圖片的資源 ID。這會更新畫面上的 ImageView,並顯示 dice_2 圖片。

rollDice() 方法現在應如下所示:

private fun rollDice() {
    val dice = Dice(6)
    val diceRoll = dice.roll()
    val diceImage: ImageView = findViewById(R.id.imageView)
    diceImage.setImageResource(R.drawable.dice_2)
}
  1. 執行應用程式,確認應用程式執行是否正常。應用程式一開始會顯示只有「Roll」按鈕的空白畫面。

c29b50554a31d30f.png

輕觸按鈕後,畫面會顯示點數為 2 的骰子圖片。就是這樣!

7df72d671b22853f.png

您可以輕觸按鈕變更圖片!您離完成不遠了!

5. 擲骰子顯示正確的骰子圖片

當然,骰子的點數不一定是 2。請運用您在「為不同 Dice Roller 新增條件式行為」程式碼研究室學習的控制流程邏輯,讓畫面根據隨機擲骰子的結果,顯示正確的骰子圖片。

開始編寫程式碼前,請編寫一些「虛擬程式碼」來說明應用程式的行為,藉此從概念上思考應用程式應該採取的行為模式。例如:

如果使用者擲出 1,即顯示 dice_1 圖片。

如果使用者擲出 2,即顯示 dice_2 圖片。

以此類推...

上述虛擬程式碼可以根據擲骰子的點數,使用在 Kotlin 中的 if / else 陳述式撰寫。

if (diceRoll == 1) {
   diceImage.setImageResource(R.drawable.dice_1)
} else if (diceRoll == 2) {
   diceImage.setImageResource(R.drawable.dice_2)
}
 ...

雖然每個案例的撰寫 if / else 都大同小異。相同的邏輯只需使用 when 陳述式,就能輕鬆表達。這是較簡單的 (程式碼較短) 做法!在應用程式中使用這個方法。

when (diceRoll) {
   1 -> diceImage.setImageResource(R.drawable.dice_1)
   2 -> diceImage.setImageResource(R.drawable.dice_2)
   ...

更新 rollDice() 方法

  1. rollDice() 方法中,刪除以下這行程式碼,也就是每次將圖片資源 ID 設為 dice_2 圖片的程式碼。
diceImage.setImageResource(R.drawable.dice_2)
  1. 以根據 diceRoll 值更新 ImageViewwhen 陳述式取代。
   when (diceRoll) {
       1 -> diceImage.setImageResource(R.drawable.dice_1)
       2 -> diceImage.setImageResource(R.drawable.dice_2)
       3 -> diceImage.setImageResource(R.drawable.dice_3)
       4 -> diceImage.setImageResource(R.drawable.dice_4)
       5 -> diceImage.setImageResource(R.drawable.dice_5)
       6 -> diceImage.setImageResource(R.drawable.dice_6)
   }

完成變更後,rollDice() 方法如下所示。

private fun rollDice() {
   val dice = Dice(6)
   val diceRoll = dice.roll()

   val diceImage: ImageView = findViewById(R.id.imageView)

   when (diceRoll) {
       1 -> diceImage.setImageResource(R.drawable.dice_1)
       2 -> diceImage.setImageResource(R.drawable.dice_2)
       3 -> diceImage.setImageResource(R.drawable.dice_3)
       4 -> diceImage.setImageResource(R.drawable.dice_4)
       5 -> diceImage.setImageResource(R.drawable.dice_5)
       6 -> diceImage.setImageResource(R.drawable.dice_6)
   }
}
  1. 執行應用程式。按一下「Roll」按鈕,將骰子圖片變更為 2 以外的其他點數。成功了!

ec209952f84b81bd.png 32fc8979b1984e00.png

最佳化程式碼

若要撰寫更簡單的程式碼,請變更下列程式碼。變更程式碼不會對應用程式的使用者造成明顯的影響,但可以縮短程式碼並減少重複。

您可能已經注意到,呼叫 diceImage.setImageResource() 在陳述式中出現 6 次。

when (diceRoll) {
    1 -> diceImage.setImageResource(R.drawable.dice_1)
    2 -> diceImage.setImageResource(R.drawable.dice_2)
    3 -> diceImage.setImageResource(R.drawable.dice_3)
    4 -> diceImage.setImageResource(R.drawable.dice_4)
    5 -> diceImage.setImageResource(R.drawable.dice_5)
    6 -> diceImage.setImageResource(R.drawable.dice_6)
}

每個案例間唯一的區別是使用的資源識別碼。換句話說,您可以建立變數,儲存使用的資源識別碼。然後,在程式碼中只要呼叫 diceImage.setImageResource() 一次,即可傳入正確的資源識別碼。

  1. 更換程式碼,以下列取代。
val drawableResource = when (diceRoll) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   6 -> R.drawable.dice_6
}

diceImage.setImageResource(drawableResource)

以下的新概念是 when 運算式可以實際回傳值。使用這個新程式碼片段後,when 運算式會傳回正確的資源 ID,並儲存在 drawableResource 變數中。這樣一來,您就可以使用這個變數更新顯示的圖片資源。

  1. 請注意,when 已加上紅色底線。如果將滑鼠遊標懸停在這個運算式上,系統會顯示錯誤訊息:「‘when' expression must be exhaustive, add necessary ‘else' branch」。

發生錯誤的原因在於 when 運算式的值已指派給 drawableResource,因此 when 必須詳盡無遺,也就是這個運算式必須處理所有可能的情況,才能確保即使您變更為 12 面的骰子,仍一律會傳回值。Android Studio 建議新增 else 分支版本。若要修正錯誤,請將案例的 6 變更為 else15 的情況都相同,但包括 6 在內的所有其他情況都會由 else 處理。

val drawableResource = when (diceRoll) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   else -> R.drawable.dice_6
}

diceImage.setImageResource(drawableResource)
  1. 執行應用程式,確保應用程式仍可正常運作。請務必充分測試,確保 1 到 6 的所有數字都會與相應的骰子圖片一併顯示。

在 ImageView 上設定適當的內容說明

您目前以圖片取代骰出的點數了,但螢幕閱讀器無法顯示骰出的點數。若要修正這個問題,在更新圖片資源後,請更新 ImageView 的內容說明。內容說明是 ImageView 中顯示的內容相關文字說明,有助螢幕閱讀器描述內容。

diceImage.contentDescription = diceRoll.toString()

螢幕閱讀器可以大聲朗讀這段內容說明,因此如果螢幕上顯示骰子擲出「6」的圖片,就會大聲朗讀內容說明為「6」。

6. 採用完善程式設計做法

建立更實用的啟動體驗

使用者首次開啟應用程式時,應用程式顯示的空白畫面 (「Roll」按鈕除外) 看來很突兀。這可能會導致使用者不清楚應用程式的功能,因此您需要變更使用者介面,在首次啟動應用程式時顯示隨機擲骰子的畫面,並建立 Activity。這樣一來,使用者就較有可能理解,只要輕觸「Roll」按鈕就能擲骰子。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val rollButton: Button = findViewById(R.id.button)
    rollButton.setOnClickListener { rollDice() }

    // Do a dice roll when the app starts
    rollDice()
}

ec209952f84b81bd.png

加上程式碼註解

在程式碼中新增一些註解,說明您編寫的程式碼有何作用。

完成上述所有變更後,您的 rollDice() 方法會如下所示。

/**
* Roll the dice and update the screen with the result.
*/
private fun rollDice() {
    // Create new Dice object with 6 sides and roll the dice
    val dice = Dice(6)
    val diceRoll = dice.roll()

    // Find the ImageView in the layout
    val diceImage: ImageView = findViewById(R.id.imageView)

    // Determine which drawable resource ID to use based on the dice roll
    val drawableResource = when (diceRoll) {
        1 -> R.drawable.dice_1
        2 -> R.drawable.dice_2
        3 -> R.drawable.dice_3
        4 -> R.drawable.dice_4
        5 -> R.drawable.dice_5
        else -> R.drawable.dice_6
    }

    // Update the ImageView with the correct drawable resource ID
    diceImage.setImageResource(drawableResource)

    // Update the content description
    diceImage.contentDescription = diceRoll.toString()
}

如需完整的 MainActivity.kt 檔案,請參閱下一個步驟的連接的 GitHub 解決方案程式碼連結。

恭喜您完成 Dice Roller 應用程式!您現在可以帶著應用程式,與好友玩一晚!

7. 解決方案程式碼

本程式碼研究室的解決方案程式碼在下方顯示的專案和模組中。

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

取得程式碼

  1. 按一下上面顯示的網址。系統會在瀏覽器中開啟專案的 GitHub 頁面。
  2. 在專案的 GitHub 頁面中,按一下「Code」按鈕開啟對話方塊。

5b0a76c50478a73f.png

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

在 Android Studio 中開啟專案

  1. 啟動 Android Studio。
  2. 在「Welcome to Android Studio」視窗中,按一下「Open an existing Android Studio project」

36cc44fcf0f89a1d.png

注意:如果 Android Studio 已開啟,請依序選取「File」>「New」>「Import Project」選單選項。

21f3eec988dcfbe9.png

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

8. 摘要

  • 使用 setImageResource() 變更 ImageView 中顯示的圖片
  • 使用 if / else 運算式或 when 運算式等控制流程陳述式,處理應用程式中不同的情況,例如在不同情況下顯示不同的圖片。

9. 瞭解詳情

10. 自行練習

請按照下列步驟執行:

  1. 在應用程式新增另一個骰子,讓一個「Roll」按鈕擲出 2 顆骰子的點數。版面配置中需要使用多少 ImageViews?這會對 MainActivity.kt 程式碼造成什麼影響?

檢查您的工作:

完成的應用程式應該可以正常執行,並顯示兩顆骰子。