1. 事前準備
在先前的程式碼研究室中,您已瞭解如何使用 ViewModel 儲存應用程式資料。ViewModel 可在設定變更時保留應用程式資料。在本程式碼研究室中,您將瞭解如何整合 LiveData 與 ViewModel 中的資料。
LiveData 類別也屬於 Android 架構元件的一部分,為可觀察的資料容器類別。
必要條件
- 如何從 GitHub 下載原始碼,並在 Android Studio 中開啟。
- 如何使用活動和片段在 Kotlin 中建構並執行基本 Android 應用程式。
- 活動和片段生命週期的運作方式。
- 如何使用
ViewModel,透過裝置設定變更保留 UI 資料。 - 如何編寫 lambda 運算式。
課程內容
- 如何在應用程式中使用
LiveData和MutableLiveData。 - 如何使用
LiveData封裝儲存在ViewModel中的資料。 - 如何新增觀察器方法以觀察
LiveData.中的變化。 - 如何在版面配置檔案中編寫繫結運算式。
建構項目
- 針對 Unscramble 應用程式中的應用程式資料 (字詞、字詞計數和分數) 使用
LiveData。 - 新增觀察器方法,在資料變更時接收通知,並自動更新打散字詞的文字檢視區塊。
- 在版面配置檔案中編寫繫結運算式,在基礎
LiveData變更時觸發。分數、字詞計數和打散字詞文字檢視區塊會自動更新。
軟硬體需求
- 已安裝 Android Studio 的電腦。
- 先前程式碼研究室的解決方案程式碼 (含
ViewModel的 Unscramble 應用程式)。
下載本程式碼研究室的範例程式碼
本程式碼研究室會使用您在先前程式碼研究室 (將資料儲存於 ViewModel 中) 中建構的 Unscramble 應用程式做為範例程式碼。
2. 範例應用程式總覽
本程式碼研究室會使用您在先前程式碼研究室中熟悉的 Unscramble 解決方案程式碼。應用程式會顯示打散的字詞,讓玩家進行重組。玩家可以嘗試任意次數來猜測正確字詞。目前字詞、玩家分數和字詞計數等應用程式資料會儲存在 ViewModel 中。不過,該應用程式的 UI 不會反映新的分數和字詞計數值。在本程式碼研究室中,您將使用 LiveData 實作缺少的功能。

3. 什麼是 Livedata
LiveData 是可觀察的資料容器類別,可感知生命週期。
LiveData 特性如下:
LiveData可保存資料;LiveData包裝函式可與任何類型的資料搭配使用。LiveData可觀察,這表示當LiveData物件保存的資料變更時,觀察器會接收通知。LiveData可感知生命週期。將觀察器附加至LiveData時,觀察器會與LifecycleOwner建立關聯 (通常是活動或片段)。LiveData只會更新處於有效生命週期狀態 (例如STARTED或RESUMED) 的觀察器。如要進一步瞭解LiveData與觀察,請參閱這篇文章。
範例程式碼中的 UI 更新
在範例程式碼中,每當您想在 UI 中顯示新的打散字詞時,系統都會明確呼叫 updateNextWordOnScreen() 方法。您可在遊戲初始化期間,以及玩家按下「提交」或「略過」按鈕時呼叫此方法。將從 onViewCreated()、restartGame()、onSkipWord() 和 onSubmitWord() 方法呼叫此方法。使用 Livedata 時,您不必從多個位置呼叫此方法來更新 UI。您只會在觀察器中執行一次。
4. 將 LiveData 新增至 currentScrambledWord
在這項工作中,您會學習如何將 GameViewModel 中的目前字詞轉換為 LiveData,以使用 LiveData, 包裝任何資料。在後續工作中,您會將觀察器新增至此類 LiveData 物件,並瞭解如何觀察 LiveData。
MutableLiveData
MutableLiveData 是 LiveData 的可變動版本,也就是可以變更儲存在其中的資料值。
- 在
GameViewModel中,將變數_currentScrambledWord的類型變更為MutableLiveData<String>。LiveData和MutableLiveData屬於一般類別,因此您需要指定其保存的資料類型。 - 將
_currentScrambledWord的變數類型變更為val,因為LiveData/MutableLiveData物件的值會保持不變,只有儲存在物件中的資料會改變。
private val _currentScrambledWord = MutableLiveData<String>()
- 將支援欄位
currentScrambledWord類型變更為LiveData<String>,因為此欄位不可變動。Android Studio 會顯示某些錯誤,您將在後續步驟中進行修正。
val currentScrambledWord: LiveData<String>
get() = _currentScrambledWord
- 如要存取
LiveData物件中的資料,請使用value屬性。在getNextWord()方法的GameViewModel中,於else區塊內,將_currentScrambledWord參照變更為_currentScrambledWord.value。
private fun getNextWord() {
...
} else {
_currentScrambledWord.value = String(tempWord)
...
}
}
5. 將觀察器附加至 LiveData 物件
在這項工作中,您將會在應用程式元件 GameFragment 中設定觀察器。您新增的觀察器會觀察應用程式資料 currentScrambledWord 的變更。LiveData 具備生命週期感知功能,代表其只會更新處於有效生命週期狀態的觀察器。因此,GameFragment 中的觀察器只會在 GameFragment 處於 STARTED 或 RESUMED 狀態時收到通知。
- 在
GameFragment中,刪除updateNextWordOnScreen()方法及其所有呼叫。您將會在LiveData中附加觀察器,因此不需要使用此方法。 - 在
onSubmitWord()中,按照下列步驟修改空的if-else區塊。完整方法應如下所示。
private fun onSubmitWord() {
val playerWord = binding.textInputEditText.text.toString()
if (viewModel.isUserWordCorrect(playerWord)) {
setErrorTextField(false)
if (!viewModel.nextWord()) {
showFinalScoreDialog()
}
} else {
setErrorTextField(true)
}
}
- 為
currentScrambledWordLiveData附加觀察器。在onViewCreated()回呼結尾的GameFragment中,於currentScrambledWord上呼叫observe()方法。
// Observe the currentScrambledWord LiveData.
viewModel.currentScrambledWord.observe()
Android Studio 會顯示缺少參數的相關錯誤。您將在下一個步驟中修正錯誤。
- 將
viewLifecycleOwner做為第一個參數傳遞至observe()方法。viewLifecycleOwner代表片段的檢視畫面生命週期。此參數可協助LiveData留意GameFragment生命週期,且只有在GameFragment處於有效狀態 (STARTED或RESUMED) 時,才通知觀察器。 - 使用
newWord函式參數將 lambda 新增為第二個參數。newWord將包含新的打散字詞值。
// Observe the scrambledCharArray LiveData, passing in the LifecycleOwner and the observer.
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord ->
})
lambda 運算式是未宣告的匿名函式,但會立即以運算式的形式傳遞。lambda 運算式一律會加上大括號 { }。
- 在 lambda 運算式的函式主體中,將
newWord指派至打散字詞文字檢視區塊。
// Observe the scrambledCharArray LiveData, passing in the LifecycleOwner and the observer.
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord ->
binding.textViewUnscrambledWord.text = newWord
})
- 編譯並執行應用程式。您的遊戲應用程式應可照常運作,但現在會在
LiveData觀察器中 (而非在updateNextWordOnScreen()方法中) 自動更新打散字詞文字檢視區塊。
6. 將觀察器附加至分數和字詞計數
如同前一個工作,在這項工作中,您會將 LiveData 新增至應用程式的其他資料、分數和字詞計數中,使 UI 在遊戲期間能夠更新分數和字詞計數的正確值。
步驟 1:使用 LiveData 包裝分數和字詞計數
- 在
GameViewModel中,將_score和_currentWordCount類別變數的類型變更為val。 - 將變數
_score和_currentWordCount的資料類型變更為MutableLiveData,並將其初始化為0。 - 將支援欄位類型變更為
LiveData<Int>。
private val _score = MutableLiveData(0)
val score: LiveData<Int>
get() = _score
private val _currentWordCount = MutableLiveData(0)
val currentWordCount: LiveData<Int>
get() = _currentWordCount
- 在
reinitializeData()方法開頭的GameViewModel中,將_score和_currentWordCount的參照變更為_score.value和_currentWordCount.value。
fun reinitializeData() {
_score.value = 0
_currentWordCount.value = 0
wordsList.clear()
getNextWord()
}
- 在
nextWord()方法內的GameViewModel中,將_currentWordCount的參照變更為_currentWordCount.value!!。
fun nextWord(): Boolean {
return if (_currentWordCount.value!! < MAX_NO_OF_WORDS) {
getNextWord()
true
} else false
}
- 在
GameViewModel的increaseScore()和getNextWord()方法中,分別將_score和_currentWordCount的參照變更為_score.value和_currentWordCount.value。Android Studio 會顯示錯誤,因為_score已不再是整數,而是LiveData,您將在後續步驟中修正此錯誤。 - 使用
plus()Kotlin 函式增加_score值,如此即可使用空值安全性執行加法。
private fun increaseScore() {
_score.value = (_score.value)?.plus(SCORE_INCREASE)
}
- 同樣地,您也可以使用
inc()Kotlin 函式,使用空值安全性將值增加一。
private fun getNextWord() {
...
} else {
_currentScrambledWord.value = String(tempWord)
_currentWordCount.value = (_currentWordCount.value)?.inc()
wordsList.add(currentWord)
}
}
- 在
GameFragment中,使用value屬性存取score的值。在showFinalScoreDialog()方法中,將viewModel.score變更為viewModel.score.value。
private fun showFinalScoreDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.congratulations))
.setMessage(getString(R.string.you_scored, viewModel.score.value))
...
.show()
}
步驟 2:將觀察器附加至分數和字詞計數
應用程式中的分數和字詞計數不會更新。在這項工作中,您將使用 LiveData 觀察器進行更新。
- 在
onViewCreated()方法的GameFragment中,刪除更新分數和字詞計數文字檢視區塊的程式碼。
移除:
binding.score.text = getString(R.string.score, 0)
binding.wordCount.text = getString(R.string.word_count, 0, MAX_NO_OF_WORDS)
- 在
onViewCreated()方法結尾的GameFragment中,為score附加觀察器。將viewLifecycleOwner做為第一個參數傳遞至觀察器,並使用 lambda 運算式做為第二個參數。在 lambda 運算式中,將新的分數做為參數傳遞,並在函式主體中,將新分數設為文字檢視區塊。
viewModel.score.observe(viewLifecycleOwner,
{ newScore ->
binding.score.text = getString(R.string.score, newScore)
})
- 在
onViewCreated()方法結尾,為currentWordCountLiveData附加觀察器。將viewLifecycleOwner做為第一個參數傳遞至觀察器,並使用 lambda 運算式做為第二個參數。在 lambda 運算式中,將新字詞計數做為參數傳遞,並在函式主體中,將新字詞計數與MAX_NO_OF_WORDS設至文字檢視區塊。
viewModel.currentWordCount.observe(viewLifecycleOwner,
{ newWordCount ->
binding.wordCount.text =
getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
})
在生命週期擁有者的生命週期期間 (即 GameFragment),當 ViewModel 內的分數和字詞計數值發生變化時,就會觸發新的觀察器。
- 執行應用程式即可見證其奧妙之處。使用一些字詞進行遊戲。畫面上的分數和字詞計數也會正確更新。請注意,這些文字檢視區塊並不會根據程式碼的部分條件進行更新。
score和currentWordCount為LiveData,且基礎值變更時,系統會自動呼叫對應的觀察器。

7. 將 LiveData 搭配資料繫結使用
在先前的工作中,您的應用程式會監聽程式碼中的資料變更。同樣地,應用程式也可以監聽版面配置中的資料變更。透過資料繫結,當可觀察的 LiveData 值變更時,系統也會通知其繫結版面配置中的 UI 元素,且可從版面配置中更新 UI。
概念:資料繫結
在先前的程式碼研究室中,您已瞭解單向的檢視繫結。您可以將檢視畫面繫結至程式碼,但無法將程式碼繫結至檢視畫面。
複習檢視繫結:
檢視繫結功能可讓您更輕鬆地存取程式碼中的檢視畫面。其會為各 XML 版面配置檔案產生繫結類別。凡是在對應版面配置中具有 ID 的檢視區塊,繫結類別的例項都會包含指向這些檢視區塊的直接參照。舉例來說,Unscramble 應用程式目前使用檢視繫結,因此使用產生的繫結類別可在程式碼中參照檢視畫面。
範例:
binding.textViewUnscrambledWord.text = newWord
binding.score.text = getString(R.string.score, newScore)
binding.wordCount.text =
getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
如果使用檢視繫結,就無法參照檢視畫面中的應用程式資料 (版面配置檔案)。您可以使用資料繫結完成這項操作。
資料繫結
資料繫結程式庫也屬於 Android Jetpack 程式庫的一部分。資料繫結使用宣告式格式將版面配置中的 UI 元件繫結至應用程式中的資料來源,稍後將在程式碼研究室中說明。
簡單來說,資料繫結會將資料 (從程式碼) 繫結至檢視區塊 + 檢視區塊繫結 (將檢視區塊繫結至程式碼)︰
在 UI 控制器中使用檢視繫結的範例
binding.textViewUnscrambledWord.text = viewModel.currentScrambledWord
在版面配置檔案中使用資料繫結的範例
android:text="@{gameViewModel.currentScrambledWord}"
以上範例說明如何使用資料繫結程式庫,直接將應用程式資料指派至版面配置檔案中的檢視畫面/小工具。請注意,指派運算式中使用 @{} 語法。
使用資料繫結的主要優點在於,您可以移除活動中的許多 UI 架構呼叫,使其更加簡單且易於維護。這還可改善應用程式效能,避免發生記憶體流失及空值指標例外狀況。
步驟 1:變更資料繫結的檢視繫結
- 在
build.gradle(Module)檔案中,啟用buildFeatures區段下的dataBinding屬性。
取代
buildFeatures {
viewBinding = true
}
為
buildFeatures {
dataBinding = true
}
當 Android Studio 顯示提示時,執行 Gradle 同步處理。
- 如要在任何 Kotlin 專案中使用資料繫結,請套用
kotlin-kapt外掛程式。此步驟已在build.gradle(Module)檔案中完成。
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
上述步驟會自動為應用程式中的每個版面配置 XML 檔案產生繫結類別,如果版面配置檔案名稱為 activity_main.xml,則自動產生的類別將會稱為 ActivityMainBinding。
步驟 2:將版面配置檔案轉換為資料繫結版面配置
資料繫結版面配置檔案略有不同,且開頭為 <layout> 的根標記,接著是選用的 <data> 元素和 view 根元素。此檢視畫面元素即為非繫結版面配置檔案中的根。
- 開啟
game_fragment.xml,選取「Code」分頁標籤。 - 如要將版面配置轉換成資料繫結版面配置,請將根元素納入
<layout>標記中。您也必須將命名空間定義 (開頭為xmlns:的屬性) 移至新的根元素。在根元素上方的<layout>標記中加入<data></data>標記。Android Studio 提供可自動執行此作業的便利方法:在根元素 (ScrollView) 上按一下滑鼠右鍵,然後依序選取「Show Context Actions」>「Convert to data binding layout」。

- 版面配置應如下所示:
<layout 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">
<data>
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
...
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
- 在
GameFragment中,在onCreateView()方法的一開始,變更binding變數的執行個體化,以使用資料繫結。
取代
binding = GameFragmentBinding.inflate(inflater, container, false)
為
binding = DataBindingUtil.inflate(inflater, R.layout.game_fragment, container, false)
- 編譯程式碼;您應能夠順利編譯。您的應用程式現在會使用資料繫結,版面配置中的檢視畫面也可存取應用程式資料。
8. 新增資料繫結變數
在這項工作中,您必須在版面配置檔案中加入屬性,以便存取 viewModel 中的應用程式資料。您將初始化程式碼中的版面配置變數。
- 在
game_fragment.xml的<data>標記中,新增名為<variable>的子標記,宣告名為gameViewModel且類型為GameViewModel的屬性。您會使用此方法將ViewModel中的資料繫結至版面配置。
<data>
<variable
name="gameViewModel"
type="com.example.android.unscramble.ui.game.GameViewModel" />
</data>
請注意,gameViewModel 的類型包含套件名稱。請確認此套件名稱與應用程式中的套件名稱相符。
- 在
gameViewModel宣告下方,在Integer類型的<data>標記中加入另一個變數,並將其命名為maxNoOfWords。您將使用此方法繫結至 ViewModel 中的變數,以儲存每場遊戲的字詞數。
<data>
...
<variable
name="maxNoOfWords"
type="int" />
</data>
- 在
onViewCreated()方法的開頭的GameFragment中,初始化版面配置變數gameViewModel和maxNoOfWords。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.gameViewModel = viewModel
binding.maxNoOfWords = MAX_NO_OF_WORDS
...
}
LiveData可觀察生命週期,因此您必須將生命週期擁有者傳遞給版面配置。在onViewCreated()方法內的GameFragment中,在繫結變數的初始化下方新增下列程式碼。
// Specify the fragment view as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = viewLifecycleOwner
提醒您,您在實作 LiveData 觀察器時,也實作類似功能。您已將 viewLifecycleOwner 做為其中一個參數傳遞給 LiveData 觀察器。
9. 使用繫結運算式
繫結運算式會寫入屬性 (例如 android:text) 版面配置內,並參照版面配置屬性。版面配置屬性會透過 <variable> 標記在資料繫結版面配置檔案的頂部進行宣告。當任何相依變數有所變更時,「DB 程式庫」將執行繫結運算式 (進而更新檢視畫面)。使用資料繫結程式庫時,此變更偵測是無須付費的最佳化功能。
繫結運算式的語法
繫結運算式以 @ 符號開頭,並加上大括號 {}。在以下範例中,TextView 文字設為 user 變數的 firstName 屬性:
範例:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
步驟 1:將繫結運算式新增至目前字詞
在此步驟中,您可以將目前的字詞文字檢視區塊繫結至 ViewModel 中的 LiveData 物件。
- 在
game_fragment.xml中,請將text屬性新增至textView_unscrambled_word文字檢視區塊。使用新的版面配置變數gameViewModel,並將@{gameViewModel.currentScrambledWord}指派給text屬性。
<TextView
android:id="@+id/textView_unscrambled_word"
...
android:text="@{gameViewModel.currentScrambledWord}"
.../>
- 在
GameFragment中,移除currentScrambledWord的LiveData觀察器程式碼:片段中不再需要使用觀察器程式碼。版面配置會直接收到LiveData的變更更新。
移除:
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
{ newWord ->
binding.textViewUnscrambledWord.text = newWord
})
- 執行您的應用程式,應用程式應可照常運作。不過,打散字詞文字檢視區塊目前使用繫結運算式更新 UI,而非
LiveData觀察器。
步驟 2:將繫結運算式新增至分數和字詞計數
資料繫結運算式的資源
資料繫結運算式可透過下列語法參照應用程式資源。
範例:
android:padding="@{@dimen/largePadding}"
在上述範例中,系統會為 padding 屬性指派 dimen.xml 資源檔案的 largePadding 值。
您也可以傳遞版面配置屬性做為資源參數。
範例:
android:text="@{@string/example_resource(user.lastName)}"
strings.xml
<string name="example_resource">Last Name: %s</string>
在上述範例中,example_resource 是具有 %s 預留位置的字串資源。您會將 user.lastName 做為資源參數傳入繫結運算式,其中 user 是版面配置變數。
在此步驟中,您會將繫結運算式新增至分數和字詞計數文字檢視區塊,並傳入資源參數。這個步驟與您為上述 textView_unscrambled_word 所做操作類似。
- 在
game_fragment.xml中,使用以下繫結運算式更新word_count文字檢視區塊的text屬性。使用word_count字串資源,並將gameViewModel.currentWordCount和maxNoOfWords做為資源參數傳入。
<TextView
android:id="@+id/word_count"
...
android:text="@{@string/word_count(gameViewModel.currentWordCount, maxNoOfWords)}"
.../>
- 使用以下繫結運算式更新
score文字檢視區塊的text屬性。使用score字串資源,並傳入gameViewModel.score做為資源參數。
<TextView
android:id="@+id/score"
...
android:text="@{@string/score(gameViewModel.score)}"
... />
- 從
GameFragment中移除LiveData觀察器。您已無需使用這些觀察器,繫結運算式會在對應LiveData變更時更新 UI。
移除:
viewModel.score.observe(viewLifecycleOwner,
{ newScore ->
binding.score.text = getString(R.string.score, newScore)
})
viewModel.currentWordCount.observe(viewLifecycleOwner,
{ newWordCount ->
binding.wordCount.text =
getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
})
- 執行應用程式,使用一些字詞進行遊戲。現在,您的程式碼會使用
LiveData和繫結運算式更新 UI。

恭喜!您已瞭解如何將 LiveData 觀察器搭配 LiveData 使用,以及將 LiveData 搭配繫結運算式使用。
10. 在啟用 TalkBack 的情況下測試 Unscramble 應用程式
在完成本課程後,您會想要建構可供眾多使用者存取的應用程式。部分使用者可能會使用 Talkback 存取及瀏覽您的應用程式。TalkBack 是 Android 裝置隨附的 Google 螢幕閱讀器。TalkBack 的互動朗讀功能讓您不看螢幕也能輕鬆使用裝置。
啟用 TalkBack 後,請確保玩家可以進行遊戲。
- 按照這些操作說明在裝置上啟用 TalkBack。
- 返回 Unscramble 應用程式。
- 請參閱操作說明,使用 Talkback 探索應用程式。向右滑動即可逐一瀏覽螢幕元素,向左滑動後即可往反方向瀏覽。在任意位置輕觸兩下即可選取。確認您可以透過滑動手勢找到應用程式中的所有元素。
- 確認 Talkback 使用者可以瀏覽畫面上的每個項目。
- 觀察 Talkback 是否嘗試將打散的字詞讀做一個字詞。這可能會讓玩家感到困惑,因為這不是真正的字詞。
- 使 Talkback 大聲讀出打散字詞中的個別字元,可獲得更優異的使用者體驗。在
GameViewModel中,將打散字詞的String轉換為Spannable字串。Spannable 字串是附加額外資訊的字串。在這個範例中,我們想要將字串與TYPE_VERBATIM的TtsSpan建立關聯,使文字轉語音引擎逐字元大聲讀出打散的字詞。 - 在
GameViewModel中,使用以下程式碼修改currentScrambledWord變數的宣告方式:
val currentScrambledWord: LiveData<Spannable> = Transformations.map(_currentScrambledWord) {
if (it == null) {
SpannableString("")
} else {
val scrambledWord = it.toString()
val spannable: Spannable = SpannableString(scrambledWord)
spannable.setSpan(
TtsSpan.VerbatimBuilder(scrambledWord).build(),
0,
scrambledWord.length,
Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
spannable
}
}
這個變數現在是 LiveData<Spannable>,而不是 LiveData<String>。您不必費心瞭解所有詳細運作方式,但實作方法會使用 LiveData 轉換,將目前打散的字詞 String 轉換為 Spannable 字串,並由無障礙服務妥善處理。在接下來的程式碼研究室中,您將進一步瞭解 LiveData 轉換,其可讓您根據對應的 LiveData 值傳回不同的 LiveData 執行個體。
- 執行 Unscramble 應用程式,透過 Talkback 探索應用程式。現在,TalkBack 應可讀出打散字詞中的個別字元。
如要進一步瞭解如何讓應用程式更容易使用,請參閱這些原則。
11. 刪除未使用的程式碼
刪除無效、未使用、不需要的解決方案程式碼是良好做法。這樣不但可以輕鬆維護程式碼,也能讓新進團隊成員更容易瞭解程式碼。
- 在
GameFragment中,刪除getNextScrambledWord()和onDetach()方法。 - 在
GameViewModel中刪除onCleared()方法。 - 刪除來源檔案頂端的任何未使用匯入項目。這些項目將會顯示為灰色。
您不再需要使用記錄陳述式,如有需要,也可將其從程式碼中刪除。
- [選擇性] 刪除在先前程式碼研究室中所新增來源檔案 (
GameFragment.kt和GameViewModel.kt) 中的Log陳述式,藉此瞭解ViewModel生命週期。
12. 解決方案程式碼
本程式碼研究室的解決方案程式碼位於下方所示專案中。
- 前往專案所在的 GitHub 存放區頁面。
- 驗證分支版本名稱與程式碼研究室中指定的分支版本名稱相符。例如,在下列螢幕截圖中,分支版本名稱為「main」。

- 在專案的 GitHub 頁面中,按一下「Code」按鈕,畫面上會出現彈出式視窗。

- 在彈出式視窗中,按一下「Download ZIP」按鈕,將專案儲存至電腦。等待下載作業完成。
- 在電腦中找到該檔案 (可能位於「下載」資料夾中)。
- 按兩下解壓縮 ZIP 檔案。這項操作會建立含有專案檔案的新資料夾。
在 Android Studio 中開啟專案
- 啟動 Android Studio。
- 在「Welcome to Android Studio」視窗中,按一下「Open」。

注意:如果 Android Studio 已開啟,請改為依序選取「File」>「Open」選單選項。

- 在檔案瀏覽器中,前往已解壓縮的專案資料夾所在的位置 (可能位於「Downloads」資料夾中)。
- 按兩下該專案資料夾。
- 等待 Android Studio 開啟專案。
- 按一下「Run」按鈕
,即可建構並執行應用程式。請確認應用程式的建構作業符合預期。
13. 摘要
LiveData可保存資料;LiveData包裝函式可與任何資料搭配使用LiveData可觀察,這表示當LiveData物件保存的資料變更時,觀察器會接收通知。LiveData可感知生命週期。將觀察器附加至LiveData時,觀察器會與LifecycleOwner建立關聯 (通常是活動或片段)。LiveData 只會更新處於有效生命週期狀態 (例如STARTED或RESUMED) 的觀察器。如要進一步瞭解LiveData與觀察,請參閱這篇文章。- 應用程式可以透過資料繫結和繫結運算式監聽版面配置中的 LiveData 變更。
- 繫結運算式會寫入屬性 (例如
android:text) 版面配置內,並參照版面配置屬性。
14. 瞭解詳情
- LiveData 總覽
- LiveData 觀察器 API 參考資料
- 資料繫結
- 雙向資料繫結
網誌文章