Dasar-Dasar Android Kotlin 05.4: Transformasi LiveData

Codelab ini adalah bagian dari kursus Dasar-Dasar Kotlin Android. Anda akan mendapatkan manfaat maksimal dari kursus ini jika Anda mengerjakan codelab secara berurutan. Semua codelab kursus tercantum di halaman landing codelab Dasar-Dasar Android Kotlin.

Pengantar

Aplikasi GuessTheWord yang Anda kerjakan di tiga codelab sebelumnya mengimplementasikan pola observer LiveData untuk mengamati data ViewModel. Tampilan di pengontrol UI mengamati LiveData di ViewModel dan memperbarui data yang akan ditampilkan.

Saat meneruskan LiveData antar-komponen, terkadang Anda mungkin ingin memetakan atau mengubah data. Kode Anda mungkin perlu melakukan penghitungan, hanya menampilkan subkumpulan data, atau mengubah rendisi data. Misalnya, untuk word LiveData, Anda dapat membuat transformasi yang menampilkan jumlah huruf dalam kata, bukan kata itu sendiri.

Anda dapat mentransformasi LiveData menggunakan metode bantuan di class Transformations:

Dalam codelab ini, Anda menambahkan penghitung mundur di aplikasi. Anda akan mempelajari cara menggunakan Transformations.map() di LiveData untuk mengubah waktu yang sudah berlalu menjadi format untuk ditampilkan di layar.

Yang harus sudah Anda ketahui

  • Cara membuat aplikasi Android dasar di Kotlin
  • Cara menggunakan objek ViewModel di aplikasi Anda
  • Cara menyimpan data menggunakan LiveData di ViewModel
  • Cara menambahkan metode observer LiveData untuk mengamati perubahan dalam data
  • Cara menggunakan data binding dengan ViewModel dan LiveData

Yang akan Anda pelajari

  • Cara menggunakan Transformations dengan LiveData

Yang akan Anda lakukan

  • Tambahkan timer untuk mengakhiri game.
  • Gunakan Transformations.map() untuk mengubah satu LiveData menjadi yang lain.

Dalam codelab Tutorial 5, Anda mengembangkan aplikasi GuessTheWord, dimulai dengan kode awal. GuessTheWord adalah game bergaya charade dua pemain, tempat pemain berkolaborasi untuk mencapai skor tertinggi.

Pemain pertama melihat kata dalam aplikasi dan memainkannya satu per satu, sehingga dia tidak akan menampilkan kata tersebut ke pemain kedua. Pemain kedua mencoba menebak kata.

Untuk memainkan game, pemain pertama membuka aplikasi di perangkat dan melihat kata, misalnya "gitar," seperti yang ditunjukkan pada screenshot di bawah ini.

Pemain pertama akan memainkannya, berhati-hatilah untuk tidak benar-benar mengucapkan kata itu sendiri.

  • Saat pemain kedua menebak kata dengan benar, pemain pertama menekan tombol Oke, yang akan meningkatkan jumlah satu kata dan menampilkan kata berikutnya.
  • Jika pemain kedua tidak dapat menebak kata, pemain pertama menekan tombol Lewati, yang mengurangi jumlah satu dan melewati ke kata berikutnya.
  • Untuk mengakhiri game, tekan tombol End Game. (Fungsi ini tidak ada dalam kode awal untuk codelab pertama dalam seri.)

Dalam codelab ini, Anda akan meningkatkan aplikasi GuessTheWord dengan menambahkan penghitung mundur satu menit yang muncul di atas skor. Timer mengakhiri game saat hitung mundur mencapai 0.

Anda juga menggunakan transformasi untuk memformat objek LiveData yang berlalu menjadi objek LiveData string timer. LiveData yang ditransformasi adalah sumber data binding untuk tampilan teks timer.

Dalam tugas ini, Anda akan menemukan dan menjalankan kode awal untuk codelab ini. Anda dapat menggunakan aplikasi GuessTheWord yang dibuat di codelab sebelumnya sebagai kode awal, atau Anda dapat mendownload aplikasi awal.

  1. (Opsional) Jika Anda tidak menggunakan kode dari codelab sebelumnya, download kode awal untuk codelab ini. Buka zip kode, lalu buka project di Android Studio.
  2. Jalankan aplikasi dan mainkan game.
  1. Perhatikan bahwa tombol Skip menampilkan kata berikutnya dan menurunkan skor sebesar satu, dan tombol Got it menunjukkan kata berikutnya dan meningkatkan skor sebesar satu. Tombol Akhiri Game mengakhiri game.
  1. Lihat semua kata, dan perhatikan bahwa aplikasi otomatis membuka layar skor.

Dalam tugas ini, Anda menambahkan penghitung mundur ke aplikasi. Alih-alih game berakhir saat daftar kata kosong, game berakhir saat timer selesai. Android menyediakan class utilitas yang disebut CountDownTimer yang Anda gunakan untuk menerapkan timer.

Tambahkan logika untuk timer di GameViewModel sehingga timer tidak dihancurkan selama perubahan konfigurasi. Fragmen berisi kode untuk memperbarui tampilan teks timer saat timer berjalan.

Terapkan langkah-langkah berikut di class GameViewModel:

  1. Buat objek companion untuk menyimpan konstanta timer.
companion object {

   // Time when the game is over
   private const val DONE = 0L

   // Countdown time interval
   private const val ONE_SECOND = 1000L

   // Total time for the game
   private const val COUNTDOWN_TIME = 60000L

}
  1. Untuk menyimpan waktu hitung mundur timer, tambahkan variabel anggota MutableLiveData yang disebut _currentTime dan properti pendukung, currentTime.
// Countdown time
private val _currentTime = MutableLiveData<Long>()
val currentTime: LiveData<Long>
   get() = _currentTime
  1. Tambahkan variabel anggota private yang disebut timer dari jenis CountDownTimer. Anda mengatasi error inisialisasi pada langkah berikutnya.
private val timer: CountDownTimer
  1. Di dalam blok init, lakukan inisialisasi dan mulai timer. Teruskan total waktu, COUNTDOWN_TIME. Untuk interval waktu, gunakan ONE_SECOND. Ganti metode callback onTick() dan onFinish() lalu mulai timer.
// Creates a timer which triggers the end of the game when it finishes
timer = object : CountDownTimer(COUNTDOWN_TIME, ONE_SECOND) {

   override fun onTick(millisUntilFinished: Long) {
       
   }

   override fun onFinish() {
       
   }
}

timer.start()
  1. Implementasikan metode callback onTick(), yang dipanggil pada setiap interval atau pada setiap centang. Perbarui _currentTime, menggunakan parameter yang diteruskan millisUntilFinished. millisUntilFinished adalah jumlah waktu hingga timer selesai dalam milidetik. Konversikan millisUntilFinished ke detik dan tetapkan ke _currentTime.
override fun onTick(millisUntilFinished: Long)
{
   _currentTime.value = millisUntilFinished/ONE_SECOND
}
  1. Metode callback onFinish() dipanggil saat timer selesai. Implementasikan onFinish() untuk mengupdate _currentTime dan memicu peristiwa penyelesaian game.
override fun onFinish() {
   _currentTime.value = DONE
   onGameFinish()
}
  1. Perbarui metode nextWord() untuk mereset daftar kata saat daftar kosong, bukan menyelesaikan game.
private fun nextWord() {
   // Shuffle the word list, if the list is empty 
   if (wordList.isEmpty()) {
       resetList()
   } else {
   // Remove a word from the list
   _word.value = wordList.removeAt(0)
}
  1. Di dalam metode onCleared(), batalkan timer untuk menghindari kebocoran memori. Anda dapat menghapus laporan log, karena laporan tersebut tidak diperlukan lagi. Metode onCleared() dipanggil sebelum ViewModel dihancurkan.
override fun onCleared() {
   super.onCleared()
   // Cancel the timer
   timer.cancel()
}
  1. Jalankan aplikasi dan mainkan game. Tunggu selama 60 detik, dan game akan selesai secara otomatis. Namun, teks timer tidak ditampilkan di layar. Anda akan memperbaikinya nanti.

Metode Transformations.map() memberikan cara untuk melakukan manipulasi data pada LiveData sumber dan menampilkan objek LiveData hasil. Transformasi ini tidak dihitung kecuali jika pengamat mengamati objek LiveData yang ditampilkan.

Metode ini menggunakan LiveData sumber dan suatu fungsi sebagai parameter. Fungsi ini memanipulasi LiveData sumber.

Dalam tugas ini, Anda memformat objek LiveData waktu yang berlalu ke objek LiveData string baru dalam format "MM:SS" Anda juga menampilkan waktu berlalu yang diformat di layar.

File tata letak game_fragment.xml sudah menyertakan tampilan teks timer. Sejauh ini, tampilan teks tidak memiliki teks untuk ditampilkan, sehingga teks timer belum terlihat.

  1. Di class GameViewModel, setelah membuat instance currentTime, buat objek LiveData baru bernama currentTimeString. Objek ini ditujukan untuk versi string berformat currentTime.
  2. Gunakan Transformations.map() untuk menentukan currentTimeString. Teruskan currentTime dan fungsi lambda untuk memformat waktu. Anda dapat menerapkan fungsi lambda menggunakan metode utilitas DateUtils.formatElapsedTime(), yang mengambil long jumlah milidetik dan memformatnya ke "MM:SS" format string.
// The String version of the current time
val currentTimeString = Transformations.map(currentTime) { time ->
   DateUtils.formatElapsedTime(time)
}
  1. Pada file game_fragment.xml, di tampilan teks timer, ikat atribut text ke currentTimeString dari gameViewModel.
<TextView
   android:id="@+id/timer_text"
   ...
   android:text="@{gameViewModel.currentTimeString}"
   ... />
  1. Jalankan aplikasi dan mainkan game. Teks timer diperbarui satu detik sekali. Perhatikan bahwa game tidak selesai jika Anda telah melihat semua kata secara bergantian. Game sekarang selesai saat timer habis.

Selamat! Anda berhasil menambahkan timer ke aplikasi yang otomatis mengakhiri game. Anda juga telah mempelajari cara menggunakan Transformations.map() untuk mengonversi satu objek LiveData ke objek lainnya.

Project Android Studio: GuessTheWord

Tantangan: Buat petunjuk tentang kata, dan tampilkan petunjuk dalam tampilan teks di atas timer. Petunjuk ini dapat memberitahukan jumlah karakter yang dimiliki kata, dan dapat menampilkan salah satu huruf pada posisi acak.

Petunjuk: Gunakan Transformations.map() pada objek word LiveData saat ini. Menambahkan TextView tambahan untuk menampilkan petunjuk kata.

  1. Di class GameViewModel, tambahkan val untuk mengubah kata saat ini menjadi petunjuk.
// The Hint for the current word
val wordHint = Transformations.map(word) { word ->
   val randomPosition = (1..word.length).random()
   "Current word has " + word.length + " letters" +
           "\nThe letter at position " + randomPosition + " is " +
           word.get(randomPosition - 1).toUpperCase()
}
  1. Di game_fragment.xml, tambahkan tampilan teks baru di atas tampilan teks timer untuk menampilkan petunjuk. Ikat atribut teks dengan wordHint yang Anda tambahkan di atas.
android:text="@{gameViewModel.wordHint}"

Mentransformasi LiveData

  • Terkadang Anda ingin mengubah hasil LiveData. Misalnya, Anda dapat memformat string Date sebagai "hours:mins:seconds," atau menampilkan jumlah item dalam daftar, bukan menampilkan daftar itu sendiri. Untuk melakukan transformasi pada LiveData, gunakan metode bantuan di class Transformations.
  • Metode Transformations.map() memberikan cara mudah untuk melakukan manipulasi data pada LiveData dan menampilkan objek LiveData lainnya. Praktik yang direkomendasikan adalah menempatkan logika pemformatan data yang menggunakan class Transformations di ViewModel bersama dengan data UI.

Menampilkan hasil transformasi di TextView

  • Pastikan data sumber ditentukan sebagai LiveData di ViewModel.
  • Tentukan variabel, misalnya newResult. Gunakan Transformation.map() untuk melakukan transformasi dan menampilkan hasilnya ke variabel.
val newResult = Transformations.map(someLiveData) { input ->
   // Do some transformation on the input live data
   // and return the new value
}
  • Pastikan file tata letak yang berisi TextView mendeklarasikan variabel <data> untuk ViewModel.
<data>
   <variable
       name="MyViewModel"
       type="com.example.android.something.MyViewModel" />
</data>
  • Di file tata letak, tetapkan atribut text dari TextView ke binding newResult dari ViewModel. Contoh:
android:text="@{SomeViewModel.newResult}"

Memformat tanggal

Kursus Udacity:

Dokumentasi developer Android:

Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Terserah instruktur untuk melakukan hal berikut:

  • Tugaskan pekerjaan rumah jika diperlukan.
  • Berkomunikasi dengan siswa cara mengirimkan tugas pekerjaan rumah.
  • Beri nilai tugas pekerjaan rumah.

Instruktur dapat menggunakan saran ini sesedikit atau sebanyak yang mereka inginkan, dan harus bebas memberikan pekerjaan rumah lain yang dirasa sesuai.

Jika Anda mengerjakan codelab ini sendiri, silakan gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.

Jawab pertanyaan berikut

Pertanyaan 1

Di class mana Anda harus menambahkan logika pemformatan data yang menggunakan metode Transformations.map() untuk mengonversi LiveData ke nilai atau format yang berbeda?

  • ViewModel
  • Fragment
  • Activity
  • MainActivity

Pertanyaan 2

Metode Transformations.map() memberikan cara mudah untuk melakukan manipulasi data pada LiveData dan menampilkan __________ .

  • Objek ViewModel
  • Objek LiveData
  • String berformat
  • Objek RoomDatabase

Pertanyaan 3

Apa saja parameter untuk metode Transformations.map()?

  • LiveData sumber dan fungsi yang akan diterapkan ke LiveData
  • Hanya sumber LiveData
  • Tidak ada parameter
  • ViewModel dan fungsi yang akan diterapkan

Pertanyaan 4

Fungsi lambda yang diteruskan ke metode Transformations.map() dijalankan di thread mana?

  • Thread utama
  • Thread latar belakang
  • UI thread
  • Di coroutine

Mulai pelajaran berikutnya: 6.1: Membuat database Room

Untuk link ke codelab lainnya dalam kursus ini, lihat halaman landing codelab Dasar-Dasar Kotlin Android.