Lớp học lập trình này thuộc khoá học Kiến thức cơ bản về Kotlin cho Android. Bạn sẽ nhận được nhiều giá trị nhất qua khoá học này nếu thực hiện các lớp học lập trình theo trình tự. Tất cả lớp học lập trình của khoá học đều được liệt kê trên trang đích của lớp học lập trình Kiến thức cơ bản về cách tạo ứng dụng Android bằng Kotlin.
Giới thiệu
Trong lớp học lập trình trước, bạn đã sử dụng ViewModel trong ứng dụng Đoán từ để cho phép dữ liệu của ứng dụng vẫn tồn tại sau khi thay đổi 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 Bộ thành phần cấu trúc Android) cho phép bạn tạo các đối tượng dữ liệu thông báo cho các khung hiển thị khi cơ sở dữ liệu cơ bản thay đổi.
Để sử dụng lớp LiveData, bạn thiết lập "trình quan sát" (ví dụ: hoạt động hoặc mảnh) để theo dõi các thay đổi về dữ liệu của ứng dụng. LiveData nhận biết được vòng đời, nên chỉ cập nhật những đối tượng tiếp nhận dữ liệu thành phần ứng dụng ở 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 điều hướng giữa các đích đến của ứng dụng.
- Vòng đời của hoạt động và mảnh.
- Cách sử dụng các đối tượng
ViewModeltrong ứng dụng. - Cách tạo các đối tượng
ViewModelbằng giao diệnViewModelProvider.Factory.
Kiến thức bạn sẽ học được
- Điều gì khiến các đối tượng
LiveDatatrở nên hữu ích. - Cách thêm
LiveDatavào dữ liệu được lưu trữ trongViewModel. - Thời điểm và cách 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
LiveDatabằng thuộc tính sao lưu. - Cách giao tiếp giữa bộ điều khiển giao diện người dùng và
ViewModeltương ứng.
Bạn sẽ thực hiện
- Sử dụng
LiveDatacho từ và điểm số trong ứng dụng GuessTheWord. - Thêm các đối tượng tiếp nhận dữ liệu để nhận biết khi từ hoặc điểm số thay đổi.
- Cập nhật các khung hiển thị văn bản hiển thị các giá trị đã thay đổi.
- Sử dụng mẫu trình quan sát
LiveDatađể thêm sự kiện kết thúc trò chơi. - Triển khai nút Phát lại.
Trong lớp học lập trình Bài 5, bạn sẽ phát triển ứng dụng GuessTheWord, bắt đầu bằng mã khởi động. GuessTheWord là một trò chơi đố chữ dành cho 2 người chơi, trong đó người chơi hợp tác với nhau để đạt điểm cao nhất có thể.
Người chơi thứ nhất nhìn vào các từ trong ứng dụng và lần lượt diễn tả từng từ, nhớ không cho người chơi thứ hai thấy từ đó. 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ừ, chẳng hạn như "guitar", như trong ảnh chụp màn hình bên dưới.
Người chơi đầu tiên diễn tả từ đó, cẩn thận không nói ra từ đó.
- Khi người chơi thứ hai đoán đúng từ, người chơi thứ nhất sẽ nhấn nút Got It (Đã hiểu). Thao tác này sẽ tăng số lượng từ lên một và cho thấy từ tiếp theo.
- Nếu người chơi thứ hai không đoán được từ, người chơi thứ nhất sẽ nhấn vào nút Bỏ qua. Thao tác này sẽ giảm số lượt đoán đi một và chuyển sang từ tiếp theo.
- Để kết thúc trò chơi, hãy nhấn vào nút Kết thúc trò chơi. (Chức năng này không có trong mã khởi đầu của lớp học lập trình đầu tiên trong loạt lớp học lập trình này.)
Trong lớp học lập trình này, bạn sẽ cải thiện ứng dụng Đoán từ bằng cách thêm một sự kiện để kết thúc trò chơi khi người dùng xem hết tất cả các từ trong ứng dụng. Bạn cũng sẽ thêm nút Chơi lại vào mảnh điểm số để 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 số |
Trong nhiệm vụ này, bạn sẽ xác định vị trí và chạy mã khởi đầu cho lớp học lập trình này. Bạn có thể dùng ứng dụng GuessTheWord mà bạn đã tạo trong lớp học lập trình trước làm mã khởi đầu hoặc tải một ứng dụng khởi đầu xuống.
- (Không bắt buộc) Nếu không sử dụng mã của mình từ lớp học lập trình trước, hãy tải mã khởi đầu xuống cho 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 Skip (Bỏ qua) sẽ chuyển sang từ tiếp theo và giảm điểm đi 1, còn nút Got It (Tôi hiểu) sẽ chuyển sang từ tiếp theo và tăng điểm thêm 1. 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 bọc LiveData xung quanh điểm số hiện tại trong ứng dụng Đoán từ. Trong lớp học lập trình này, bạn sẽ tìm hiểu về một số đặc điểm của LiveData:
LiveDatacó 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ượngLiveDatagiữ có thay đổi.LiveDatagiữ dữ liệu;LiveDatalà một trình bao bọc có thể được sử dụng với bất kỳ dữ liệu nàoLiveDatanhậ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 ở trạng thái vòng đời hoạt động, chẳng hạn nhưSTARTEDhoặcRESUMED.
Trong nhiệm vụ này, bạn sẽ tìm hiểu cách tổng hợp mọi kiểu dữ liệu thành các đối tượng LiveData bằng cách chuyển đổi dữ liệu điểm số hiện tại và từ hiện tại trong GameViewModel thành LiveData. Trong một nhiệm vụ sau, bạn sẽ 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 của các biến
scorevàwordthànhMutableLiveData.MutableLiveDatalà mộtLiveDatacó thể thay đổi giá trị.MutableLiveDatalà một lớp chung, do vậy, bạn cần chỉ định loại dữ liệu mà lớp này 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ạyscorevà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 cách sử dụng thuộc tínhvalue.
init {
word.value = ""
score.value = 0
...
}Bước 2: Cập nhật tham chiếu đối tượng LiveData
Các biến score và word hiện thuộc loại LiveData. Trong bước này, bạn sẽ thay đổi các giá trị tham chiếu đến những 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 đổiscorethànhscore.value. Lưu ý lỗi về việcscorecó thể lànull. Bạn sẽ khắc phục lỗi này ở phần tiếp theo. - Để giải quyết lỗi này, hãy thêm một chế độ kiểm tra
nullvàoscore.valuetrongonSkip(). Sau đó, hãy gọi hàmminus()trênscore, hàm này sẽ thực hiện phép trừ với độ an toànnull.
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 một chế độ kiểm tranullvào biếnscorevà 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 tham chiếuwordthà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 tham chiếu thànhviewModel.wordthà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 tham chiếu đếnviewModel.scorethà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 tham chiếu thànhviewModel.scorethànhviewModel.score.value. Thêm lệnh kiểm tra an toànnullbắ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 rằng mã của bạn không có lỗi. Biên dịch và chạy ứng dụng. Chức năng của ứng dụng sẽ giống như trước đây.
Nhiệm vụ này có liên quan chặt chẽ đến nhiệm vụ trước, trong đó bạn đã chuyển đổi dữ liệu điểm số và từ thành các đối tượng LiveData. Trong nhiệm vụ này, bạn sẽ đính kèm các đố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ượngObservervào đối tượngLiveDatacho điểm số hiện tại,viewModel.score. Sử dụng phương thứcobserve()và đặt mã sau khi khởi chạyviewModel. Sử dụng biểu thức lambda để đơn giản hoá mã. (Biểu thức lambda là một hàm ẩn danh không được khai báo nhưng được chuyển ngay dưới dạng một biểu thức.)
viewModel.score.observe(this, Observer { newScore ->
})Giải quyết vấn đề trùng lặp tệp đối chiếu với Observer. Để thực hiện việc 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 sẽ nhận được một sự kiện khi dữ liệu do đối tượng
LiveDatađược quan sát giữ có thay đổi. Bên trong trình quan sát, hãy cập nhật điểm sốTextViewbằ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
Observervào đối tượngLiveDatacủa từ hiện tại. Làm theo cách tương tự như cách bạn đính kèm một đối tượngObservervà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 hiển thị trên màn hình sẽ tự động cập nhật.
- Trong
GameFragment, hãy xoá 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 đến các đối tượng tiếp nhận dữ liệu đó nữa, vì các phương thức của đối tượng tiếp nhận dữ liệuLiveDatasẽ cập nhật khung hiển thị văn bản. - Chạy ứng dụng. Ứng dụng trò chơi của bạn sẽ hoạt động chính xác như trước, nhưng giờ đây, ứng dụng sẽ sử dụng các đối tượng tiếp nhận dữ liệu
LiveDatavàLiveData.
Đó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 một đố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, 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, chẳng hạn như sử dụng viewModel.score.value. Điều này có thể không quan trọng trong ứng dụng mà bạn đang phát triển trong lớp học lập trình này, nhưng trong một ứng dụng phát hành công khai, bạn muốn kiểm soát dữ liệu trong các đối tượng ViewModel.
Chỉ ViewModel mới được phép chỉnh sửa dữ liệu trong ứng dụng của bạn. Tuy nhiên, các trình điều khiển giao diện người dùng cần đọ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 đấu với LiveData:
- Bạn có thể thay đổi dữ liệu trong đối tượng
MutableLiveData, như tên cho thấy. Bên trongViewModel, dữ liệu phải có thể chỉnh sửa được, vậy nên dữ liệu sẽ sử dụngMutableLiveData. - Bạn có thể đọc dữ liệu trong đối tượng
LiveDatanhưng không thể thay đổi. Bên ngoàiViewModel, dữ liệu phải có thể đọc được nhưng không thể chỉnh sửa được, vậy nên dữ liệu phải xuất hiện dưới dạngLiveData.
Để thực hiện chiến lược này, bạn sẽ sử dụng một thuộc tính sao lưu 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 sẽ triển khai một thuộc tính hỗ trợ cho các đối tượng score và word trong ứng dụng Đoán từ.
Thêm một thuộc tính sao lưu vào điểm số và từ
- Trong
GameViewModel, hãy tạo đối tượngscorehiện tạiprivate. - Để tuân theo quy ước đặt tên được dùng trong các thuộc tính sao lưu, hãy thay đổi
scorethành_score. Thuộc tính_scorehiện là phiên bản có thể thay đổi của điểm số trò chơi, được dùng nội bộ. - Tạo một phiên bản công khai của loại
LiveData, có tên làscore.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>- Bạn thấy lỗi khởi chạy. Lỗi này xảy ra vì bên trong
GameFragment,scorelà một tham chiếuLiveDatavàscorekhông thể truy cập vào phương thức thiết lập của nó nữa. Để tìm hiểu thêm về các phương thức getter và setter trong Kotlin, hãy xem bài viết Getters and Setters (Các phương thức getter và setter).
Để giải quyết lỗi này, hãy ghi đè phương thứcget()cho đối tượngscoretrongGameViewModelvà trả về thuộc tính hỗ trợ_score.
val score: LiveData<Int>
get() = _score- Trong
GameViewModel, hãy thay đổi các tham chiếu củascorethành phiên bản có thể thay đổi nội bộ của nó,_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)
}
...
}- Đổi tên đối tượng
wordthành_wordvà thêm một thuộc tính sao lưu cho đối tượng đó, như bạn đã làm cho đố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)
}
}Tuyệt vời, bạn đã đóng gói các đối tượng LiveData word và score.
Ứng dụng hiện tại của bạn sẽ chuyển đến màn hình điểm số khi người dùng nhấn vào nút End Game (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 số khi người chơi đã chơi hết tất cả các từ. Sau khi người chơi hoàn thành từ cuối cùng, bạn muốn trò chơi tự động kết thúc để 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 có một sự kiện được kích hoạt và truyền đến mảnh từ ViewModel khi tất cả các từ đã xuất hiện. Để làm việc này, bạn sẽ sử dụng mẫu trình quan sát LiveData để mô hình hoá 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. Mẫu này chỉ định hoạt động kết nối giữa các đối tượng: một đố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. Đố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ó.

Trong trường hợp LiveData trong ứng dụng này, đối tượng có thể quan sát (đối tượng) là đối tượng LiveData và các đối tượng quan sát là các phương thức trong đơn vị điều khiển giao diện người dùng, chẳng hạn như các mảnh. Trạng thái thay đổi mỗi khi dữ liệu được bao bọc bên trong LiveData thay đổi. Các lớp LiveData rất 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 sự kiện kết thúc trò chơi
Trong nhiệm vụ này, bạn sẽ sử dụng mẫu trình quan sát LiveData để mô hình hoá một sự kiện kết thúc trò chơi.
- Trong
GameViewModel, hãy tạo một đối tượngBooleanMutableLiveDatacó tên là_eventGameFinish. Đối tượng này sẽ lưu giữ sự kiện kết thúc trò chơi. - Sau khi khởi động đối tượng
_eventGameFinish, hãy tạo và khởi động một thuộc tính hỗ trợ 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 này, hãy đặt sự kiện kết thúc trò chơieventGameFinishthà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 chạyviewModel, 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à xem hết tất cả các từ. Ứng dụng sẽ tự động chuyển đến màn hình điểm số thay vì ở trong đoạn mã 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 đặt, phương thức trình quan sát được 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 gây ra vấn đề về vòng đời. Để hiểu rõ vấn đề, trong lớp
GameFragment, hãy đánh dấu ghi chú vào mã điều hướng trong phương thứcgameFinished(). Hãy nhớ giữ thông báoToasttrong phương thức này.
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à xem hết tất cả các từ. Một thông báo ngắn cho biết "Trận đấu vừa kết thúc" sẽ xuất hiện trong thời gian ngắn ở cuối màn hình trận đấu. Đây là hành vi dự kiến.
Bây giờ, hãy xoay thiết bị hoặc trình mô phỏng. Thông báo sẽ xuất hiện lại! Xoay thiết bị thêm vài lần nữa, có thể bạn sẽ thấy thông báo mỗi lần. Đây là một lỗi, vì thông báo chỉ được hiển thị một lần, khi trò chơi kết thúc. Thông báo tạm thời không được hiển thị mỗi khi phân mảnh được tạo lại. Bạn sẽ giải quyết vấn đề này trong nhiệm vụ tiếp theo.
|
|
Bước 2: Đặt lại sự kiện kết thúc trò chơi
Thông thường, LiveData chỉ phân phối thông tin cập nhật cho các trình quan sát khi dữ liệu thay đổi. Hành vi này có một ngoại lệ là trình quan sát cũng sẽ nhận được thông tin cập nhật khi thay đổi trạng thái từ không hoạt động sang đang hoạt động.
Đây là lý do thông báo dạng toast về việc hoàn thành 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 này sẽ chuyển từ trạng thái không hoạt động sang trạng thái hoạt động. Đối tượng theo dõi trong mảnh được kết nối lại với ViewModel hiện có và nhận được dữ liệu hiện tại. Phương thức gameFinished() được kích hoạt lại và thông báo hiển thị.
Trong nhiệm vụ này, bạn sẽ khắc phục vấn đề này và chỉ hiển thị thông báo 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, khi kết thúcgameFinished(), hãy gọionGameFinishComplete()trên đối tượngviewModel. (Tạm thời để mã điều hướng tronggameFinished()ở trạng thái có nhận xét.)
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}- Chạy ứng dụng và chơi trò chơi. Đọc hết tất cả các từ, sau đó thay đổi hướng màn hình của thiết bị. Thông báo chỉ xuất hiện một lần.
- Trong
GameFragment, bên trong phương thứcgameFinished(), hãy bỏ chú thích mã điều hướng.
Để bỏ chú thích trong Android Studio, hãy chọn các dòng được chú thích rồi nhấnControl+/(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. Đảm bảo rằng ứng dụng tự động chuyển đến màn hình điểm số cuối cùng sau khi bạn chơi xong tất cả các từ.
|
|
Tuyệt vời! Ứng dụng của bạn sử dụng LiveData để kích hoạt sự kiện kết thúc trò chơi nhằm giao tiếp từ GameViewModel đến mảnh trò chơi rằng danh sách từ trống. Sau đó, phân mảnh trò chơi sẽ chuyển đến phân mảnh điểm số.
Trong nhiệm vụ này, bạn sẽ 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 đó. Nhiệm vụ này tương tự như những gì 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 tính đầy đủ, sao cho tất cả dữ liệu trong ứng dụng của bạn đều sử dụng LiveData.
- Trong
ScoreViewModel, hãy thay đổi loại biếnscorethànhMutableLiveData. Đổi tên theo quy ước thành_scorevà thêm một thuộc tính sao lưu.
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ể xoá hoặc giữ lại nhật ký trong khốiinittuỳ ý.
init {
_score.value = finalScore
}- Trong
ScoreFragment, bên trongonCreateView(), sau khi khởi chạyviewModel, 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. Xoá mã trực tiếp chỉ định chế độ xem văn bản bằng giá trị điểm số khỏiViewModel.
Mã cần thêm:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})Đoạn mã cần xoá:
binding.scoreText.text = viewModel.score.toString()Khi được Android Studio nhắc, hãy nhập androidx.lifecycle.Observer.
- Chạy ứng dụng và chơi trò chơi. Ứng dụng sẽ hoạt động như trước, nhưng giờ đây, ứng dụng sẽ sử dụng
LiveDatavà 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 sẽ thêm nút Play Again (Chơi lại) vào màn hình điểm số và triển khai trình nghe lượt nhấp của nút này bằng sự kiện LiveData. Nút này kích hoạt một sự kiện để chuyển từ màn hình điểm số sang màn hình trò chơi.
Đoạn mã khởi đầu cho ứng dụng có nút Play Again (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ínhvisibilitythànhvisible.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>- Trong
ScoreViewModel, hãy thêm một đối tượngLiveDatađể lưu giữ mộtBooleancó tên là_eventPlayAgain. Đối tượng này dùng để lưu sự kiệnLiveDatanhằm chuyển từ màn hình điểm số sang 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 nghe lượt nhấp vào nút PlayAgain (Phát lại) và gọiviewModel.onPlayAgain().
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }- Chạy ứng dụng và chơi trò chơi. Khi trò chơi kết thúc, màn hình điểm số sẽ cho thấy điểm số cuối cùng và nút Chơi lại. Nhấn vào nút PlayAgain (Chơi lại) và ứng dụng sẽ chuyển đến màn hình trò chơi để bạn có thể chơi lại.

Chúc mừng bạn! Bạn đã thay đổi cấu trúc của ứng dụng để dùng các đối tượng LiveData trong ViewModel và bạn đã đính kèm các đối tượng theo dõi vào các đối tượng LiveData. LiveData thông báo cho các đối tượng tiếp nhận dữ liệu khi giá trị do LiveData giữ thay đổi.
Dự án Android Studio: GuessTheWord
LiveData
LiveDatalà 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, một trong những Thành phần cấu trúc Android.- Bạn có thể dùng
LiveDatađể cho phép giao diện người dùng tự động cập nhật khi dữ liệu cập nhật. LiveDatacó thể quan sát được, tức là một đối tượng tiếp nhận dữ liệu (chẳng hạn như một hoạt động hoặc một mảnh) có thể nhận được thông báo khi dữ liệu do đối tượngLiveDatagiữ có thay đổi.LiveDatagiữ dữ liệu; đây là một trình bao bọc có thể được dùng với mọi dữ liệu.LiveDatanhậ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 ở trạng thái vòng đời hoạt động, chẳng hạn nhưSTARTEDhoặcRESUMED.
Cách thêm LiveData
- Thay đổi loại biến dữ liệu trong
ViewModelthànhLiveDatahoặcMutableLiveData.
MutableLiveData là một đối tượng LiveData có thể thay đổi giá trị. MutableLiveData là một lớp chung, do vậy, bạn cần chỉ định loại dữ liệu mà lớp này giữ.
- Để thay đổi giá trị của dữ liệu do
LiveDatanắm giữ, hãy dùng phương thứcsetValue()trên biếnLiveData.
Để đóng gói LiveData
- Bạn có thể chỉnh sửa
LiveDatabên trongViewModel. Bên ngoàiViewModel,LiveDataphải đọc được. Bạn có thể triển khai việc này bằng cách sử dụng một thuộc tính sao lưu của Kotlin. - Thuộc tính sao lưu Kotlin 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.
- Để đóng gói
LiveData, hãy dùngprivateMutableLiveDatabên trongViewModelvà trả về một thuộc tính hỗ trợLiveDatabên ngoàiViewModel.
LiveData có thể quan sát
LiveDatatuân theo mẫu của đối tượng tiếp nhận dữ liệu. "Đối tượng có thể quan sát" là đối tượngLiveDatavà các đối tượng quan sát là các phương thức trong đơn vị điều khiển giao diện người dùng, chẳng hạn như các mảnh. Mỗi khi dữ liệu được bao bọc bên trongLiveDatathay đổi, các phương thức tiếp nhận dữ liệu trong trình điều khiển giao diện người dùng sẽ nhận đượ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ếuLiveDatatrong các đố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(). - Bạn có thể dùng mẫu đối tượng tiếp nhận dữ liệu
LiveDatanày để giao tiếp từViewModelđến các bộ điều khiển giao diện người dùng.
Khoá học của Udacity:
Tài liệu dành cho nhà phát triển Android:
Khác:
- Thuộc tính sao lưu trong Kotlin
Phần này liệt kê các bài tập về nhà cho học viên của lớp học lập trình này trong phạm vi khoá học có người hướng dẫn. Người hướng dẫn phải thực hiện các việc sau đây:
- Giao bài tập về nhà nếu cần.
- Trao đổi với học viên về 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 các đề xuất này ít hoặc nhiều tuỳ ý và nên giao cho học viên 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ự học các lớp học lập trình, hãy sử dụng những bài tập về nhà này để kiểm tra kiến thức của mình.
Trả lời các câu hỏi sau
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 của dữ liệu thànhprivateLiveData. Sử dụng thuộc tính sao lưu để hiển thị dữ liệu chỉ có thể đọc thuộc loạiMutableLiveData. - Bên trong đối tượng
ViewModel, hãy thay đổi loại của dữ liệu thànhprivateMutableLiveData. 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 trình điều khiển giao diện người dùng, hãy thay đổi loại của dữ liệu thành
privateMutableLiveData. 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 của 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
- Đã tạm dừng
- Đã dừng
Câu hỏi 3
Trong mẫu quan sát LiveData, mục quan sát (mục được quan sát) 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 khoá học này, hãy xem trang đích của lớp học lập trình Kiến thức cơ bản về cách tạo ứng dụng Android bằng Kotlin.




