Lớp học lập trình này nằm trong khóa học về Khái niệm cơ bản về Android Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự. Tất cả các lớp học lập trình trong khóa học đều có trên trang đích của các lớp học lập trình cơ bản về Android Kotlin.
Giới thiệu
Trong lớp học lập trình trước đây, bạn đã sử dụng ViewModel
trong ứng dụng GuessTheWord để cho phép dữ liệu của ứng dụng tồn tại trong các thay đổi về cấu hình thiết bị. Trong lớp học lập trình này, bạn sẽ tìm hiểu cách tích hợp LiveData
với dữ liệu trong các lớp ViewModel
. LiveData
, một trong những Thành phần cấu trúc Android, cho phép bạn xây dựng các đối tượng dữ liệu để thông báo cho chế độ xem khi cơ sở dữ liệu cơ bản thay đổi.
Để dùng lớp LiveData
, bạn thiết lập "observers" (ví dụ: các hoạt động hoặc mảnh) quan sát những thay đổi trong dữ liệu của ứng dụng. LiveData
nhận biết được vòng đời, do đó, tính năng này chỉ cập nhật những đối tượng tiếp nhận dữ liệu thành phần ứng dụng đang ở trạng thái vòng đời đang hoạt động.
Kiến thức bạn cần có
- Cách tạo các ứng dụng Android cơ bản trong Kotlin.
- Cách di chuyển giữa các đích của ứng dụng.
- Vòng đời của hoạt động và mảnh.
- Cách sử dụng đối tượng
ViewModel
trong ứng dụng của bạn. - Cách tạo đối tượng
ViewModel
bằng giao diệnViewModelProvider.Factory
.
Kiến thức bạn sẽ học được
- Điều gì khiến đối tượng
LiveData
trở nên hữu ích. - Cách thêm
LiveData
vào dữ liệu được lưu trữ trongViewModel
. - Thời điểm và cách thức sử dụng
MutableLiveData
. - Cách thêm phương thức tiếp nhận dữ liệu để quan sát các thay đổi trong
LiveData.
- Cách đóng gói
LiveData
bằng một thuộc tính hỗ trợ. - Cách kết nối giữa bộ điều khiển giao diện người dùng và
ViewModel
tương ứng của bộ điều khiển.
Bạn sẽ thực hiện
- Sử dụng
LiveData
cho từ này và điểm số trong ứng dụng GuessTheWord. - Thêm đối tượng tiếp nhận dữ liệu để ý đến khi từ hoặc điểm số thay đổi.
- Cập nhật các chế độ xem văn bản hiển thị các giá trị đã thay đổi.
- Sử dụng mẫu đối tượng tiếp nhận dữ liệu
LiveData
để thêm một sự kiện đã hoàn thành trò chơi. - Triển khai nút Phát lại.
Trong các lớp học lập trình của Bài học 5, bạn phát triển ứng dụng GuessTheWord, bắt đầu bằng mã dành cho người mới bắt đầu. GuessTheWord là trò chơi theo phong cách charades Hai người chơi, trong đó người chơi cộng tác để đạt được điểm số cao nhất có thể.
Người chơi đầu tiên nhìn vào các từ trong ứng dụng và lần lượt thực hiện từng từ, đảm bảo không hiển thị từ đó cho người chơi thứ hai. Người chơi thứ hai cố gắng đoán từ.
Để chơi trò chơi, người chơi đầu tiên mở ứng dụng trên thiết bị và thấy một từ, ví dụ như "guitar," như minh họa trong ảnh chụp màn hình dưới đây.
Người chơi đầu tiên thực hiện từ này, cẩn thận để không thực sự nói từ đó.
- Khi người chơi thứ hai đoán đúng từ, người chơi đầu tiên nhấn nút Tôi hiểu để tăng số lượng và tính từ tiếp theo.
- Nếu người chơi thứ hai không thể đoán từ, người chơi đầu tiên sẽ nhấn nút Bỏ qua để giảm một từ và bỏ qua từ tiếp theo.
- Để kết thúc trò chơi, hãy nhấn nút Kết thúc trò chơi. (Chức năng này không có trong mã bắt đầu cho lớp học lập trình đầu tiên trong chuỗi.)
Trong lớp học lập trình này, bạn cải thiện ứng dụng GuessTheWord bằng cách thêm một sự kiện để kết thúc trò chơi khi người dùng chuyển qua tất cả các từ trong ứng dụng. Bạn cũng có thể thêm nút Chơi lại trong mảnh điểm để người dùng có thể chơi lại trò chơi.
Màn hình tiêu đề | Màn hình trò chơi | Màn hình điểm |
Trong nhiệm vụ này, bạn xác định vị trí và chạy mã khởi động cho lớp học lập trình này. Bạn có thể sử dụng ứng dụng GuessTheWord mà bạn đã tích hợp trong lớp học lập trình trước đó làm mã dành cho người mới bắt đầu hoặc bạn có thể tải ứng dụng dành cho người mới bắt đầu xuống.
- (Không bắt buộc) Nếu bạn không sử dụng mã trong lớp học lập trình trước đó, hãy tải mã dành cho người mới bắt đầu xuống trong lớp học lập trình này. Giải nén mã rồi mở dự án trong Android Studio.
- Chạy ứng dụng và chơi trò chơi.
- Lưu ý rằng nút Bỏ qua hiển thị từ tiếp theo và giảm điểm số một, đồng thời nút Tôi hiểu hiển thị từ tiếp theo và tăng điểm số một. Nút Kết thúc trò chơi sẽ kết thúc trò chơi.
LiveData
là một lớp lưu giữ dữ liệu có thể quan sát được và nhận biết được vòng đời. Ví dụ: bạn có thể bao gồm một LiveData
xung quanh điểm số hiện tại trong ứng dụng GuessTheWord. Trong lớp học lập trình này, bạn tìm hiểu về một số đặc điểm của LiveData
:
LiveData
có thể quan sát được, tức là đối tượng tiếp nhận dữ liệu sẽ nhận được thông báo khi dữ liệu được đối tượngLiveData
giữ có thay đổi.LiveData
giữ dữ liệu;LiveData
là một trình bao bọc có thể được sử dụng với bất kỳ dữ liệu nàoLiveData
nhận biết được vòng đời, nghĩa là chỉ cập nhật những đối tượng tiếp nhận dữ liệu đang ở trạng thái vòng đời đang hoạt động nhưSTARTED
hoặcRESUMED
.
Trong nhiệm vụ này, bạn sẽ tìm hiểu cách thêm bất kỳ loại dữ liệu nào vào đối tượng LiveData
bằng cách chuyển đổi điểm hiện tại và dữ liệu từ hiện tại trong GameViewModel
thành LiveData
. Trong một nhiệm vụ sau đó, bạn thêm một đối tượng tiếp nhận dữ liệu vào các đối tượng LiveData
này và tìm hiểu cách quan sát LiveData
.
Bước 1: Thay đổi điểm số và từ để sử dụng LiveData
- Trong gói
screens/game
, hãy mở tệpGameViewModel
. - Thay đổi loại biến
score
vàword
thànhMutableLiveData
.MutableLiveData
là mộtLiveData
có giá trị có thể thay đổi được.MutableLiveData
là lớp chung, do đó, bạn cần chỉ định loại dữ liệu mà lớp đó lưu giữ.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
- Trong
GameViewModel
, bên trong khốiinit
, hãy khởi chạyscore
vàword
. Để thay đổi giá trị của biếnLiveData
, bạn dùng phương thứcsetValue()
trên biến đó. Trong Kotlin, bạn có thể gọisetValue()
bằng thuộc tínhvalue
.
init {
word.value = ""
score.value = 0
...
}
Bước 2: Cập nhật thông tin tham chiếu đối tượng của LiveData
Biến score
và word
hiện thuộc loại LiveData
. Ở bước này, bạn thay đổi các thông tin tham chiếu đến các biến này bằng cách sử dụng thuộc tính value
.
- Trong
GameViewModel
, trong phương thứconSkip()
, hãy thay đổiscore
thànhscore.value
. Xin lưu ý rằng lỗi vềscore
có thể lànull
. Bạn sẽ khắc phục lỗi này tiếp theo. - Để giải quyết lỗi này, hãy thêm một dấu kiểm
null
vàoscore.value
trongonSkip()
. Sau đó, gọi hàmminus()
trênscore
để thực hiện phép trừ bằngnull
-safety.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}
- Cập nhật phương thức
onCorrect()
theo cách tương tự: thêm dấu kiểmnull
vào biếnscore
và sử dụng hàmplus()
.
fun onCorrect() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.plus(1)
}
nextWord()
}
- Trong
GameViewModel
, bên trong phương thứcnextWord()
, hãy thay đổi tệp đối chiếuword
thànhword
.
value
.
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word.value = wordList.removeAt(0)
}
}
- Trong
GameFragment
, bên trong phương thứcupdateWordText()
, hãy thay đổi tệp đối chiếu thànhviewModel
.word
thànhviewModel
.
word
.
value.
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}
- Trong
GameFragment
, bên trong phương thứcupdateScoreText()
, hãy thay đổi tệp đối chiếu thànhviewModel
.score
thànhviewModel
.
score
.
value.
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}
- Trong
GameFragment
, bên trong phương thứcgameFinished()
, hãy thay đổi tệp đối chiếu thànhviewModel
.score
thànhviewModel
.
score
.
value
. Thêm tính năng kiểm tra an toànnull
bắt buộc.
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
NavHostFragment.findNavController(this).navigate(action)
}
- Đảm bảo không có lỗi trong mã của bạn. Biên dịch và chạy ứng dụng của bạn. Chức năng của ứng dụng phải giống như trước đây.
Việc cần làm này có liên quan chặt chẽ với việc cần làm trước đó, trong đó bạn đã chuyển đổi điểm số và dữ liệu từ thành các đối tượng LiveData
. Trong việc cần làm này, bạn đính kèm đối tượng Observer
vào các đối tượng LiveData
đó.
- Trong
GameFragment,
bên trong phương thứconCreateView()
, hãy đính kèm một đối tượngObserver
vào đối tượngLiveData
cho điểm hiện tại,viewModel.score
. Hãy sử dụng phương thứcobserve()
và đặt mã sau khi khởi chạyviewModel
. Hãy dùng một biểu thức lambda để đơn giản hóa mã. (Biểu thức lambda là hàm ẩn danh không được khai báo nhưng được chuyển ngay dưới dạng biểu thức.)
viewModel.score.observe(this, Observer { newScore ->
})
Giải quyết tệp đối chiếu thành Observer
. Để làm điều này, hãy nhấp vào Observer
, nhấn Alt+Enter
(Option+Enter
trên máy Mac) rồi nhập androidx.lifecycle.Observer
.
- Đối tượng tiếp nhận dữ liệu mà bạn vừa tạo nhận được một sự kiện khi dữ liệu do đối tượng
LiveData
quan sát được thay đổi. Bên trong đối tượng tiếp nhận dữ liệu, hãy cập nhật điểm sốTextView
bằng điểm số mới.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Đính kèm một đối tượng
Observer
vào đối tượng từ hiện tạiLiveData
. Làm tương tự như cách bạn đã đính kèm đối tượngObserver
vào điểm số hiện tại.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
Khi giá trị của score
hoặc word
thay đổi, score
hoặc word
xuất hiện trên màn hình sẽ tự động cập nhật.
- Trong
GameFragment
, hãy xóa các phương thứcupdateWordText()
vàupdateScoreText()
cũng như tất cả các tệp tham chiếu đến các phương thức đó. Bạn không cần sử dụng nữa vì các chế độ xem văn bản được cập nhật bằng phương thức quan sátLiveData
. - Chạy ứng dụng của bạn. Ứng dụng trò chơi của bạn sẽ hoạt động giống như trước đây, nhưng giờ đây, ứng dụng sẽ sử dụng
LiveData
vàLiveData
đối tượng tiếp nhận dữ liệu.
Đóng gói là một cách để hạn chế quyền truy cập trực tiếp vào một số trường của đối tượng. Khi đóng gói một đối tượng, bạn cho thấy một tập hợp phương thức công khai có chức năng sửa đổi các trường nội bộ riêng tư. Bằng cách đóng gói, bạn có thể kiểm soát cách các lớp khác điều khiển những trường nội bộ này.
Trong mã hiện tại của bạn, mọi lớp bên ngoài đều có thể sửa đổi các biến score
và word
bằng cách sử dụng thuộc tính value
, ví dụ như bằng cách sử dụng viewModel.score.value
. Quy trình này có thể không quan trọng trong ứng dụng mà bạn đang phát triển, nhưng trong ứng dụng chính thức, bạn cần kiểm soát dữ liệu trong các đối tượng ViewModel
.
Chỉ ViewModel
mới chỉnh sửa được dữ liệu trong ứng dụng của bạn. Tuy nhiên, bộ điều khiển giao diện người dùng cần phải đọc dữ liệu, vì vậy, các trường dữ liệu không thể hoàn toàn riêng tư. Để đóng gói dữ liệu của ứng dụng, bạn sử dụng cả đối tượng MutableLiveData
và LiveData
.
MutableLiveData
so với LiveData
:
- Bạn có thể thay đổi dữ liệu trong đối tượng
MutableLiveData
, như được ngụ ý trong tên. Bên trongViewModel
, dữ liệu phải được chỉnh sửa để sử dụngMutableLiveData
. - Bạn có thể đọc nhưng không thể thay đổi dữ liệu trong đối tượng
LiveData
. Từ bên ngoàiViewModel
, dữ liệu phải dễ đọc nhưng không thể chỉnh sửa, vì vậy dữ liệu phải hiển thị dưới dạngLiveData
.
Để triển khai chiến lược này, bạn hãy sử dụng thuộc tính dự phòng của Kotlin. Thuộc tính sao lưu cho phép bạn trả về nội dung qua một phương thức getter khác với đối tượng chính xác. Trong nhiệm vụ này, bạn triển khai thuộc tính hỗ trợ cho các đối tượng score
và word
trong ứng dụng GuessTheWord.
Thêm một thuộc tính hỗ trợ vào điểm số và từ
- Trong
GameViewModel
, hãy đặt đối tượngscore
hiện tại thànhprivate
. - Để tuân theo quy ước đặt tên dùng trong các thuộc tính dự phòng, hãy thay đổi
score
thành_score
. Thuộc tính_score
hiện là phiên bản có thể thay đổi của điểm số trong trò chơi để sử dụng nội bộ. - Tạo một phiên bản công khai của loại
LiveData
, được gọi làscore
.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
- Bạn gặp lỗi khởi chạy. Lỗi này xảy ra vì trong
GameFragment
,score
là tệp tham chiếuLiveData
vàscore
không thể truy cập vào phương thức setter nữa. Để tìm hiểu thêm về phương thức getter và phương thức setter trong Kotlin, hãy xem bài viết Các phương thức getter và setter.
Để giải quyết lỗi, hãy ghi đè phương thứcget()
cho đối tượngscore
trongGameViewModel
và trả về thuộc tính sao lưu,_score
.
val score: LiveData<Int>
get() = _score
- Trong
GameViewModel
, hãy thay đổi các tệp tham chiếu củascore
thành phiên bản có thể thay đổi nội bộ của_score
.
init {
...
_score.value = 0
...
}
...
fun onSkip() {
if (!wordList.isEmpty()) {
_score.value = (score.value)?.minus(1)
}
...
}
fun onCorrect() {
if (!wordList.isEmpty()) {
_score.value = (score.value)?.plus(1)
}
...
}
- Hãy đổi tên đối tượng
word
thành_word
và thêm thuộc tính hỗ trợ cho đối tượng này, như đối với đối tượngscore
.
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
get() = _word
...
init {
_word.value = ""
...
}
...
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
_word.value = wordList.removeAt(0)
}
}
Thật tuyệt, bạn đã đóng gói các đối tượng LiveData
word
và score
.
Ứng dụng hiện tại sẽ chuyển đến màn hình điểm khi người dùng nhấn vào nút Kết thúc trò chơi. Bạn cũng muốn ứng dụng chuyển đến màn hình điểm khi người chơi di chuyển qua tất cả các từ. Sau khi người chơi kết thúc bằng từ cuối cùng, bạn muốn trò chơi kết thúc tự động để người dùng không phải nhấn vào nút.
Để triển khai chức năng này, bạn cần một sự kiện được kích hoạt và thông báo cho mảnh từ ViewModel
khi tất cả các từ đã được hiển thị. Để làm điều này, bạn sử dụng mẫu đối tượng tiếp nhận dữ liệu LiveData
để lập mô hình một sự kiện đã kết thúc trò chơi.
Mẫu đối tượng tiếp nhận dữ liệu
Mẫu đối tượng tiếp nhận dữ liệu là một mẫu thiết kế phần mềm. Lệnh này chỉ định việc giao tiếp giữa các đối tượng: đối tượng phát ra dữ liệu ("chủ thể" quan sát) và đối tượng tiếp nhận dữ liệu. Một đối tượng phát ra dữ liệu là một đối tượng thông báo cho các đối tượng tiếp nhận dữ liệu về những thay đổi liên quan đến trạng thái của nó.
Đối với LiveData
trong ứng dụng này, đối tượng phát ra dữ liệu (chủ thể) là đối tượng LiveData
, còn đối tượng tiếp nhận dữ liệu là các phương thức trong bộ điều khiển giao diện người dùng, chẳng hạn như các mảnh. Trạng thái sẽ thay đổi bất cứ khi nào dữ liệu được gói bên trong LiveData
thay đổi. Các lớp LiveData
có vai trò quan trọng trong việc kết nối giữa ViewModel
với mảnh.
Bước 1: Sử dụng LiveData để phát hiện một sự kiện đã kết thúc trò chơi
Trong nhiệm vụ này, bạn dùng mẫu quan sát LiveData
để lập mô hình một sự kiện đã kết thúc trò chơi.
- Trong
GameViewModel
, hãy tạo một đối tượngBoolean
MutableLiveData
có tên là_eventGameFinish
. Đối tượng này sẽ lưu giữ sự kiện đã hoàn thành trò chơi. - Sau khi khởi tạo đối tượng
_eventGameFinish
, hãy tạo và khởi tạo một thuộc tính sao lưu có tên làeventGameFinish
.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish
- Trong
GameViewModel
, hãy thêm phương thứconGameFinish()
. Trong phương thức, hãy đặt sự kiện đã hoàn thành trò chơieventGameFinish
thànhtrue
.
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}
- Trong
GameViewModel
, bên trong phương thứcnextWord()
, hãy kết thúc trò chơi nếu danh sách từ trống.
private fun nextWord() {
if (wordList.isEmpty()) {
onGameFinish()
} else {
//Select and remove a _word from the list
_word.value = wordList.removeAt(0)
}
}
- Trong
GameFragment
, bên trongonCreateView()
, sau khi khởi tạoviewModel
, hãy đính kèm một đối tượng tiếp nhận dữ liệu vàoeventGameFinish
. Sử dụng phương thứcobserve()
. Bên trong hàm lambda, hãy gọi phương thứcgameFinished()
.
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
if (hasFinished) gameFinished()
})
- Chạy ứng dụng, chơi trò chơi và khám phá tất cả các từ. Ứng dụng sẽ tự động chuyển đến màn hình điểm, thay vì ở trong mảnh trò chơi cho đến khi bạn nhấn vào Kết thúc trò chơi.
Sau khi danh sách từ trống,eventGameFinish
được thiết lập, phương thức quan sát liên kết trong mảnh trò chơi sẽ được gọi và ứng dụng sẽ chuyển đến mảnh màn hình. - Mã bạn thêm đã dẫn đến một vấn đề về vòng đời. Để hiểu vấn đề, trong lớp
GameFragment
, hãy nhận xét mã điều hướng trong phương thứcgameFinished()
. Hãy đảm bảo giữ lại thông báoToast
trong phương thức.
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
// val action = GameFragmentDirections.actionGameToScore()
// action.score = viewModel.score.value?:0
// NavHostFragment.findNavController(this).navigate(action)
}
- Chạy ứng dụng, chơi trò chơi và khám phá tất cả các từ. Một thông báo ngắn có nội dung "Trò chơi vừa kết thúc" sẽ xuất hiện nhanh ở cuối màn hình trò chơi, đây là hành vi dự kiến.
Bây giờ, hãy xoay thiết bị hoặc trình mô phỏng. Chào mừng bạn! Xoay thiết bị vài lần nữa và có thể bạn sẽ luôn thấy thông báo nhanh. Đây là một lỗi vì thông báo nhanh chỉ hiển thị một lần khi trò chơi kết thúc. Bánh mì nướng sẽ không hiển thị mỗi khi mảnh được tạo lại. Bạn giải quyết vấn đề này trong nhiệm vụ tiếp theo.
Bước 2: Đặt lại sự kiện đã hoàn thành trò chơi
Thông thường, LiveData
sẽ chỉ cập nhật cho đối tượng tiếp nhận dữ liệu khi dữ liệu thay đổi. Một trường hợp ngoại lệ đối với hành vi này là các đối tượng tiếp nhận dữ liệu cũng nhận được các bản cập nhật khi đối tượng tiếp nhận dữ liệu thay đổi từ trạng thái không hoạt động sang trạng thái đang hoạt động.
Đây là lý do tại sao bánh mì nướng đã hoàn tất trò chơi được kích hoạt nhiều lần trong ứng dụng của bạn. Khi mảnh trò chơi được tạo lại sau khi xoay màn hình, mảnh sẽ chuyển từ trạng thái không hoạt động sang trạng thái đang hoạt động. Trình quan sát trong mảnh được kết nối lại với ViewModel
hiện có và nhận dữ liệu hiện tại. Phương thức gameFinished()
được kích hoạt lại và thông báo nhanh sẽ hiển thị.
Trong tác vụ này, bạn khắc phục sự cố này và chỉ hiển thị thông báo nhanh một lần bằng cách đặt lại cờ eventGameFinish
trong GameViewModel
.
- Trong
GameViewModel
, hãy thêm phương thứconGameFinishComplete()
để đặt lại sự kiện đã kết thúc trò chơi,_eventGameFinish
.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
- Trong
GameFragment
, vào cuốigameFinished()
, hãy gọionGameFinishComplete()
trên đối tượngviewModel
. (Còn bây giờ, hãy để lại mã điều hướng tronggameFinished()
được nhận xét.)
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}
- Chạy ứng dụng và chơi trò chơi. Xem qua tất cả các từ, sau đó thay đổi hướng màn hình của thiết bị. Bánh mì nướng chỉ hiển thị một lần.
- Trong
GameFragment
, ở bên trong phương thứcgameFinished()
, hãy hủy nhận xét mã điều hướng.
Để hủy nhận xét trong Android Studio, hãy chọn các dòng được nhận xét và nhấn vào biểu tượngControl+/
(Command+/
trên máy Mac).
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
findNavController(this).navigate(action)
viewModel.onGameFinishComplete()
}
Nếu Android Studio nhắc, hãy nhập androidx.navigation.fragment.NavHostFragment.findNavController
.
- Chạy ứng dụng và chơi trò chơi. Hãy đảm bảo rằng ứng dụng tự động chuyển đến màn hình điểm cuối cùng sau khi bạn xem hết tất cả các từ.
Thật tuyệt! Ứng dụng của bạn dùng LiveData
để kích hoạt một sự kiện đã hoàn thành trò chơi để kết nối từ GameViewModel
đến mảnh trò chơi mà danh sách từ trống. Sau đó, mảnh trò chơi chuyển đến mảnh điểm.
Trong nhiệm vụ này, bạn thay đổi điểm số thành một đối tượng LiveData
trong ScoreViewModel
và đính kèm một đối tượng tiếp nhận dữ liệu vào đối tượng đó. Việc cần làm này tương tự như việc bạn đã làm khi thêm LiveData
vào GameViewModel
.
Bạn thực hiện những thay đổi này đối với ScoreViewModel
để đảm bảo hoàn chỉnh, nhờ đó, tất cả dữ liệu trong ứng dụng của bạn sử dụng LiveData
.
- Trong
ScoreViewModel
, hãy thay đổi loại biếnscore
thànhMutableLiveData
. Đổi tên theo quy ước thành_score
rồi thêm một thuộc tính hỗ trợ.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score
- Trong
ScoreViewModel
, bên trong khốiinit
, hãy khởi chạy_score
. Bạn có thể xóa hoặc để lại nhật ký trong khốiinit
theo ý muốn.
init {
_score.value = finalScore
}
- Trong
ScoreFragment
, bên trongonCreateView()
, sau khi khởi tạoviewModel
, hãy đính kèm một đối tượng tiếp nhận dữ liệu cho đối tượng điểm sốLiveData
. Bên trong biểu thức lambda, hãy đặt giá trị điểm thành chế độ xem văn bản điểm. Xóa mã chỉ định trực tiếp chế độ xem văn bản bằng giá trị điểm khỏiViewModel
.
Mã cần thêm:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
Mã cần xóa:
binding.scoreText.text = viewModel.score.toString()
Khi được Android Studio nhắc, hãy nhập androidx.lifecycle.Observer
.
- Chạy ứng dụng của bạn và chơi trò chơi. Ứng dụng sẽ hoạt động như trước đây, nhưng giờ đây, ứng dụng sẽ dùng
LiveData
và một đối tượng tiếp nhận dữ liệu để cập nhật điểm số.
Trong nhiệm vụ này, bạn thêm nút Phát lại vào màn hình điểm số và triển khai trình xử lý lượt nhấp bằng cách sử dụng sự kiện LiveData
. Nút này sẽ kích hoạt một sự kiện để chuyển từ màn hình điểm sang màn hình trò chơi.
Mã khởi động cho ứng dụng bao gồm nút Phát lại nhưng nút này bị ẩn.
- Trong
res/layout/score_fragment.xml
, đối với nútplay_again_button
, hãy thay đổi giá trị của thuộc tínhvisibility
thànhvisible
.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>
- Trong
ScoreViewModel
, hãy thêm đối tượngLiveData
để lưu giữBoolean
có tên_eventPlayAgain
. Đối tượng này được dùng để lưu sự kiệnLiveData
để chuyển từ màn hình điểm đến màn hình trò chơi.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgain
- Trong
ScoreViewModel
, hãy xác định các phương thức để đặt và đặt lại sự kiện,_eventPlayAgain
.
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}
- Trong
ScoreFragment
, hãy thêm một đối tượng tiếp nhận dữ liệu choeventPlayAgain
. Đặt mã ở cuốionCreateView()
, trước câu lệnhreturn
. Bên trong biểu thức lambda, hãy quay lại màn hình trò chơi và đặt lạieventPlayAgain
.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
if (playAgain) {
findNavController().navigate(ScoreFragmentDirections.actionRestart())
viewModel.onPlayAgainComplete()
}
})
Nhập androidx.navigation.fragment.findNavController
khi được Android Studio nhắc.
- Trong
ScoreFragment
, bên trongonCreateView()
, hãy thêm một trình xử lý lượt nhấp vào nút PlayAgain và gọiviewModel
.onPlayAgain()
.
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Chạy ứng dụng của bạn và chơi trò chơi. Khi trò chơi kết thúc, màn hình điểm sẽ hiển thị điểm cuối cùng và nút Chơi lại. Nhấn vào nút PlayAgain rồi ứng dụng đó chuyển đến màn hình trò chơi để bạn có thể chơi lại trò chơi.
Rất tốt! Bạn đã thay đổi cấu trúc của ứng dụng để sử dụng các đối tượng LiveData
trong ViewModel
và đã đính kèm đối tượng tiếp nhận dữ liệu vào đối tượng LiveData
. LiveData
sẽ thông báo cho các đối tượng tiếp nhận dữ liệu khi giá trị do LiveData
thay đổi.
Dự án Android Studio: GuessTheWord
LiveData
LiveData
là một lớp lưu giữ dữ liệu có thể quan sát, là một trong các Thành phần cấu trúc Android.- Bạn có thể sử dụng
LiveData
để cho phép giao diện người dùng của mình tự động cập nhật khi dữ liệu cập nhật. - Có thể quan sát
LiveData
, nghĩa là một đối tượng tiếp nhận dữ liệu như hoạt động hoặc một mảnh có thể được thông báo khi dữ liệu do đối tượngLiveData
lưu giữ thay đổi. LiveData
chứa dữ liệu; đó là một trình bao bọc có thể dùng với bất kỳ dữ liệu nào.LiveData
nhận biết được vòng đời, nghĩa là chỉ cập nhật những đối tượng tiếp nhận dữ liệu đang ở trạng thái vòng đời đang hoạt động nhưSTARTED
hoặcRESUMED
.
Cách thêm LiveData
- Thay đổi loại biến dữ liệu trong
ViewModel
thànhLiveData
hoặcMutableLiveData
.
MutableLiveData
là đối tượng LiveData
có thể thay đổi giá trị. MutableLiveData
là lớp chung, do đó, bạn cần chỉ định loại dữ liệu mà lớp đó lưu giữ.
- Để thay đổi giá trị của dữ liệu do
LiveData
lưu giữ, hãy sử dụng phương thứcsetValue()
trên biếnLiveData
.
Để đóng gói LiveData
- Bạn phải chỉnh sửa được
LiveData
bên trongViewModel
. Bên ngoàiViewModel
,LiveData
phải dễ đọc. Bạn có thể triển khai lệnh này bằng thuộc tính dự phòng của Kotlin. - Thuộc tính sao lưu Kotlin cho phép bạn trả về nội dung từ phương thức getter khác với đối tượng chính xác.
- Để đóng gói
LiveData
, hãy sử dụngprivate
MutableLiveData
bên trongViewModel
và trả về một thuộc tính hỗ trợLiveData
bên ngoàiViewModel
.
LiveData có thể quan sát
LiveData
tuân theo một mẫu đối tượng tiếp nhận dữ liệu. "obobable" là đối tượngLiveData
và đối tượng tiếp nhận dữ liệu là các phương thức trong bộ điều khiển giao diện người dùng, như các mảnh. Bất cứ khi nào dữ liệu được gói bên trongLiveData
thay đổi, các phương thức quan sát trong bộ điều khiển giao diện người dùng sẽ được thông báo.- Để có thể quan sát
LiveData
, hãy đính kèm một đối tượng tiếp nhận dữ liệu vào tham chiếuLiveData
trong đối tượng tiếp nhận dữ liệu (chẳng hạn như hoạt động và mảnh) bằng phương thứcobserve()
. - Có thể dùng mẫu đối tượng tiếp nhận dữ liệu
LiveData
này để giao tiếp từViewModel
đến bộ điều khiển giao diện người dùng.
Khóa học từ Udacity:
Tài liệu dành cho nhà phát triển Android:
Các tài liệu khác:
- Thuộc tính dự phòng trong Kotlin
Phần này liệt kê các bài tập về nhà có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:
- Giao bài tập về nhà nếu được yêu cầu.
- Trao đổi với học viên cách nộp bài tập về nhà.
- Chấm điểm bài tập về nhà.
Người hướng dẫn có thể sử dụng những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.
Nếu bạn đang tự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.
Trả lời những câu hỏi này
Câu hỏi 1
Bạn đóng gói LiveData
lưu trữ trong ViewModel
như thế nào để các đối tượng bên ngoài có thể đọc nhưng không thể cập nhật dữ liệu?
- Bên trong đối tượng
ViewModel
, hãy thay đổi loại dữ liệu thànhprivate
LiveData
. Sử dụng thuộc tính sao lưu để hiển thị dữ liệu chỉ đọc thuộc loạiMutableLiveData
. - Bên trong đối tượng
ViewModel
, hãy thay đổi loại dữ liệu thànhprivate
MutableLiveData
. Sử dụng thuộc tính sao lưu để hiển thị dữ liệu chỉ đọc thuộc loạiLiveData
. - Bên trong bộ điều khiển giao diện người dùng, hãy thay đổi loại dữ liệu thành
private
MutableLiveData
. Sử dụng thuộc tính sao lưu để hiển thị dữ liệu chỉ có thể đọc thuộc loạiLiveData
. - Bên trong đối tượng
ViewModel
, hãy thay đổi loại dữ liệu thànhLiveData
. Sử dụng thuộc tính sao lưu để hiển thị dữ liệu chỉ có thể đọc thuộc loạiLiveData
.
Câu hỏi 2
LiveData
sẽ cập nhật trình điều khiển giao diện người dùng (chẳng hạn như một mảnh) nếu trình này ở một trong các trạng thái sau đây?
- Đã tiếp tục
- Trong chế độ nền
- Bị tạm dừng
- Đã dừng
Câu hỏi 3
Trong mẫu đối tượng tiếp nhận dữ liệu LiveData
, mục quan sát được là gì?
- Phương thức quan sát
- Dữ liệu trong đối tượng
LiveData
- Trình điều khiển giao diện người dùng
- Đối tượng
ViewModel
Bắt đầu bài học tiếp theo:
Để biết đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích của các lớp học lập trình cơ bản về Android Kotlin.