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)
}
}
- 為
currentScrambledWord
LiveData
附加觀察器。在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()
方法結尾,為currentWordCount
LiveData
附加觀察器。將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 參考資料
- 資料繫結
- 雙向資料繫結
網誌文章