Menggunakan LiveData dengan ViewModel

1. Sebelum memulai

Anda telah mempelajari di codelab sebelumnya cara menggunakan ViewModel untuk menyimpan data aplikasi. ViewModel memungkinkan data aplikasi bertahan saat terjadi perubahan konfigurasi. Dalam codelab ini, Anda akan mempelajari cara mengintegrasikan LiveData dengan data di ViewModel.

Class LiveData juga merupakan bagian dari Komponen Arsitektur Android dan merupakan class holder data yang dapat diamati.

Prasyarat

  • Cara mendownload kode sumber dari GitHub dan membukanya di Android Studio.
  • Cara membuat dan menjalankan aplikasi Android dasar di Kotlin, dengan menggunakan aktivitas dan fragmen.
  • Cara kerja siklus proses aktivitas dan fragmen.
  • Cara mempertahankan data UI melalui perubahan konfigurasi perangkat menggunakan ViewModel.
  • Cara menulis ekspresi lambda.

Yang akan Anda pelajari

  • Cara menggunakan LiveData dan MutableLiveData di aplikasi Anda.
  • Cara mengenkapsulasi data yang disimpan dalam ViewModel dengan LiveData.
  • Cara menambahkan metode observer untuk mengamati perubahan di LiveData.
  • Cara menulis ekspresi binding di file tata letak.

Yang akan Anda build

  • Gunakan LiveData untuk data aplikasi (kata, jumlah kata, dan skor) di aplikasi Unscramble.
  • Tambahkan metode observer yang mendapatkan notifikasi saat data berubah, perbarui tampilan teks kata acak secara otomatis.
  • Tulis ekspresi binding dalam file tata letak, yang dipicu saat LiveData yang mendasarinya diubah. Skor, jumlah kata, dan tampilan teks kata yang diacak diperbarui secara otomatis.

Yang Anda perlukan

  • Komputer yang dilengkapi Android Studio.
  • Kode solusi dari codelab sebelumnya (aplikasi Unscramble dengan ViewModel).

Mendownload kode awal untuk codelab ini

Codelab ini menggunakan aplikasi Unscramble yang Anda build di codelab sebelumnya (Menyimpan data di ViewModel) sebagai kode awal.

2. Ringkasan aplikasi awal

Codelab ini menggunakan kode solusi Unscramble yang Anda kenal dari codelab sebelumnya. Aplikasi menampilkan kata acak untuk disusun ulang oleh pemain. Pemain dapat mencoba beberapa kali untuk menebak kata yang benar. Data aplikasi seperti kata saat ini, skor pemain, dan jumlah kata akan disimpan di ViewModel. Namun, UI aplikasi tidak mencerminkan nilai skor dan nilai jumlah kata yang baru. Dalam codelab ini, Anda akan menerapkan fitur yang hilang menggunakan LiveData.

a20e6e45e0d5dc6f.png

3. Apa yang dimaksud dengan Livedata

LiveData adalah class holder data yang dapat diamati dan peka terhadap siklus proses.

Beberapa karakteristik LiveData:

  • LiveData menyimpan data; LiveData adalah wrapper yang dapat digunakan dengan semua jenis data.
  • LiveData dapat diamati, yang berarti bahwa observer akan diberi tahu saat data yang dimiliki objek LiveData berubah.
  • LiveData mendukung siklus proses. Saat Anda melampirkan observer ke LiveData, observer dikaitkan dengan LifecycleOwner (biasanya berupa aktivitas atau fragmen). LiveData hanya mengupdate observer yang dalam status siklus proses aktif seperti STARTED atau RESUMED. Anda dapat membaca info selengkapnya tentang LiveData dan observasi di sini.

Proses update UI di kode awal

Dalam kode awal, metode updateNextWordOnScreen() dipanggil secara eksplisit, setiap kali Anda ingin menampilkan kata acak baru di UI. Anda memanggil metode ini selama inisialisasi game, dan saat pemain menekan tombol Submit atau Skip. Metode ini dipanggil dari metode onViewCreated(), restartGame(), onSkipWord(), dan onSubmitWord(). Dengan Livedata, Anda tidak perlu memanggil metode ini dari beberapa tempat untuk mengupdate UI. Anda hanya akan melakukannya sekali di observer.

4. Menambahkan LiveData ke kata acak saat ini

Dalam tugas ini, Anda akan mempelajari cara menggabungkan data dengan LiveData, dengan mengonversi kata saat ini di GameViewModel menjadi LiveData. Pada tugas selanjutnya, Anda akan menambahkan observer ke objek LiveData ini dan mempelajari cara mengobservasi LiveData.

MutableLiveData

MutableLiveData adalah versi LiveData yang bisa berubah, yaitu, nilai data yang tersimpan di dalamnya dapat diubah.

  1. Di GameViewModel, ubah jenis variabel _currentScrambledWord menjadi MutableLiveData<String>. LiveData dan MutableLiveData adalah class generik, jadi Anda perlu menentukan jenis data yang disimpannya.
  2. Ubah jenis variabel _currentScrambledWord menjadi val karena nilai objek LiveData/MutableLiveData akan tetap sama, dan hanya data yang disimpan dalam objek yang akan berubah.
private val _currentScrambledWord = MutableLiveData<String>()
  1. Ubah jenis kolom cadangan currentScrambledWord ke LiveData<String>, karena tidak dapat diubah. Android Studio akan menampilkan beberapa error yang akan Anda perbaiki pada langkah berikutnya.
val currentScrambledWord: LiveData<String>
   get() = _currentScrambledWord
  1. Untuk mengakses data dalam objek LiveData, gunakan properti value. Di GameViewModel dalam metode getNextWord(), dalam blok else, ubah referensi _currentScrambledWord menjadi _currentScrambledWord.value.
private fun getNextWord() {
 ...
   } else {
       _currentScrambledWord.value = String(tempWord)
       ...
   }
}

5. Melampirkan observer ke objek LiveData

Dalam tugas ini, Anda menyiapkan observer di komponen aplikasi, GameFragment. Observer yang akan Anda tambahkan mengobservasi perubahan pada data aplikasi currentScrambledWord. LiveData berbasis siklus proses, yang berarti hanya mengupdate observer yang ada dalam status siklus proses aktif. Jadi, observer dalam GameFragment hanya akan diberi tahu jika GameFragment dalam status STARTED atau RESUMED.

  1. Di GameFragment, hapus metode updateNextWordOnScreen() dan semua panggilan ke dalamnya. Anda tidak memerlukan metode ini, karena Anda akan melampirkan observer ke LiveData.
  2. Di onSubmitWord(), ubah blok if-else kosong sebagai berikut. Metode yang lengkap akan terlihat seperti ini.
private fun onSubmitWord() {
    val playerWord = binding.textInputEditText.text.toString()

    if (viewModel.isUserWordCorrect(playerWord)) {
        setErrorTextField(false)
        if (!viewModel.nextWord()) {
            showFinalScoreDialog()
        }
    } else {
        setErrorTextField(true)
    }
}
  1. Lampirkan observer untuk currentScrambledWord LiveData. Pada GameFragment di akhir callback onViewCreated(), panggil metode observe() pada currentScrambledWord.
// Observe the currentScrambledWord LiveData.
viewModel.currentScrambledWord.observe()

Android Studio akan menampilkan error tentang parameter yang tidak ada. Anda akan memperbaiki error tersebut di langkah berikutnya.

  1. Teruskan viewLifecycleOwner sebagai parameter pertama ke metode observe(). viewLifecycleOwner merepresentasikan siklus proses Tampilan Fragment. Parameter ini membantu LiveData mengetahui siklus proses GameFragment dan memberi tahu observer hanya jika GameFragment dalam status aktif (STARTED atau RESUMED).
  2. Tambahkan lambda sebagai parameter kedua dengan newWord sebagai parameter fungsi. newWord akan berisi nilai kata acak yang baru.
// Observe the scrambledCharArray LiveData, passing in the LifecycleOwner and the observer.
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
   { newWord ->
   })

Ekspresi lambda adalah fungsi anonim yang tidak dideklarasikan, tetapi langsung diteruskan sebagai ekspresi. Ekspresi lambda selalu dikelilingi oleh tanda kurung kurawal { }.

  1. Dalam isi fungsi ekspresi lambda, tetapkan newWord ke tampilan teks kata acak.
// Observe the scrambledCharArray LiveData, passing in the LifecycleOwner and the observer.
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
   { newWord ->
       binding.textViewUnscrambledWord.text = newWord
   })
  1. Kompilasi dan jalankan aplikasi. Aplikasi game Anda seharusnya bekerja persis seperti sebelumnya, tetapi sekarang tampilan teks kata acak akan diperbarui secara otomatis di observer LiveData, bukan di metode updateNextWordOnScreen().

6. Memasang observer ke skor dan jumlah kata

Seperti pada tugas sebelumnya, dalam tugas ini, Anda akan menambahkan LiveData ke data lain di aplikasi, skor, dan jumlah kata, sehingga UI diupdate dengan nilai skor dan jumlah kata yang benar selama game.

Langkah 1: Gabungkan skor dan jumlah kata dengan LiveData

  1. Dalam GameViewModel, ubah jenis variabel class _score dan _currentWordCount menjadi val.
  2. Ubah jenis data variabel _score dan _currentWordCount menjadi MutableLiveData, lalu inisialisasikan ke 0.
  3. Ubah jenis kolom cadangan ke LiveData<Int>.
private val _score = MutableLiveData(0)
val score: LiveData<Int>
   get() = _score

private val _currentWordCount = MutableLiveData(0)
val currentWordCount: LiveData<Int>
   get() = _currentWordCount
  1. Di GameViewModel pada awal metode reinitializeData(), ubah referensi _score dan _currentWordCount masing-masing menjadi _score.value dan _currentWordCount.value.
fun reinitializeData() {
   _score.value = 0
   _currentWordCount.value = 0
   wordsList.clear()
   getNextWord()
}
  1. Di GameViewModel, dalam metode nextWord(), ubah referensi _currentWordCount menjadi _currentWordCount.value!!.
fun nextWord(): Boolean {
    return if (_currentWordCount.value!! < MAX_NO_OF_WORDS) {
           getNextWord()
           true
       } else false
   }
  1. Di GameViewModel, di dalam metode increaseScore() dan getNextWord(), ubah referensi _score dan _currentWordCount menjadi _score.value dan _currentWordCount.value. Android Studio akan menampilkan error karena _score tidak lagi berupa bilangan bulat, namun LiveData, dan Anda akan memperbaikinya di langkah berikutnya.
  2. Gunakan fungsi Kotlin plus() untuk meningkatkan nilai _score, yang menjalankan penambahan dengan keamanan null.
private fun increaseScore() {
    _score.value = (_score.value)?.plus(SCORE_INCREASE)
}
  1. Demikian pula, gunakan fungsi Kotlin inc() untuk meningkatkan nilai satu per satu dengan keamanan null.
private fun getNextWord() {
   ...
    } else {
        _currentScrambledWord.value = String(tempWord)
        _currentWordCount.value = (_currentWordCount.value)?.inc()
        wordsList.add(currentWord)
       }
   }
  1. Di GameFragment, akses nilai score menggunakan properti value. Dalam metode showFinalScoreDialog(), ubah viewModel.score menjadi viewModel.score.value.
private fun showFinalScoreDialog() {
   MaterialAlertDialogBuilder(requireContext())
       .setTitle(getString(R.string.congratulations))
       .setMessage(getString(R.string.you_scored, viewModel.score.value))
       ...
       .show()
}

Langkah 2: Lampirkan observer ke skor dan jumlah kata

Di aplikasi, skor dan jumlah kata tidak diperbarui. Anda akan memperbaruinya dalam tugas ini menggunakan observer LiveData.

  1. Di GameFragment dalam metode onViewCreated(), hapus kode yang memperbarui tampilan teks skor dan jumlah kata.

Hapus:

binding.score.text = getString(R.string.score, 0)
binding.wordCount.text = getString(R.string.word_count, 0, MAX_NO_OF_WORDS)
  1. Pada GameFragment di akhir metode onViewCreated(), lampirkan observer untuk score. Teruskan viewLifecycleOwner sebagai parameter pertama ke observer dan ekspresi lambda untuk parameter kedua. Di dalam ekspresi lambda, teruskan skor baru sebagai parameter dan di dalam isi fungsi, setel skor baru ke tampilan teks.
viewModel.score.observe(viewLifecycleOwner,
   { newScore ->
       binding.score.text = getString(R.string.score, newScore)
   })
  1. Di akhir metode onViewCreated(), lampirkan observer untuk currentWordCount LiveData. Teruskan viewLifecycleOwner sebagai parameter pertama ke observer dan ekspresi lambda untuk parameter kedua. Di dalam ekspresi lambda, teruskan jumlah kata baru sebagai parameter dan di isi fungsi, tetapkan jumlah kata baru dengan MAX_NO_OF_WORDS pada tampilan teks.
viewModel.currentWordCount.observe(viewLifecycleOwner,
   { newWordCount ->
       binding.wordCount.text =
           getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
   })

Observer baru akan dipicu saat nilai skor dan jumlah kata diubah di dalam ViewModel, selama masa pakai pemilik siklus proses, yaitu, GameFragment.

  1. Jalankan aplikasi Anda untuk melihat keajaibannya. Mainkan game dengan beberapa kata. Skor dan jumlah kata juga diperbarui dengan benar di layar. Perhatikan bahwa Anda tidak memperbarui tampilan teks ini berdasarkan beberapa kondisi dalam kode. score dan currentWordCount adalah LiveData dan observer yang sesuai secara otomatis dipanggil saat nilai yang mendasarinya berubah.

80e118245bdde6df.png

7. Menggunakan LiveData dengan data binding

Pada tugas sebelumnya, aplikasi Anda memproses perubahan data dalam kode. Demikian pula, aplikasi dapat memproses perubahan data dari tata letak. Dengan Data Binding, saat nilai LiveData yang dapat diobservasi berubah, elemen UI dalam tata letak yang terikat juga akan diberi tahu, dan UI dapat diperbarui dari dalam tata letak.

Konsep: Data binding

Dalam codelab sebelumnya, Anda telah melihat View Binding, yang merupakan binding satu arah. Anda dapat mengikat tampilan ke kode namun tidak sebaliknya.

Mengingat kembali View binding:

View binding adalah fitur yang memungkinkan Anda mengakses tampilan dalam kode dengan lebih mudah. Ini menghasilkan class binding untuk setiap file tata letak XML. Instance class binding berisi referensi langsung ke semua tampilan yang memiliki ID di tata letak yang terkait. Misalnya, aplikasi Unscramble saat ini menggunakan view binding, sehingga tampilan dapat direferensikan dalam kode menggunakan class binding yang dihasilkan.

Contoh:

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)

Dengan menggunakan view binding, Anda tidak dapat mereferensikan data aplikasi dalam tampilan (file tata letak). Hal ini dapat dilakukan menggunakan Data binding.

Data Binding

Library Data Binding juga merupakan bagian dari library Android Jetpack. Data binding mengikat komponen UI dalam tata letak ke sumber data dalam aplikasi menggunakan format deklaratif, yang akan Anda pelajari nanti dalam codelab.

Dalam istilah yang lebih sederhana, Data binding adalah mengikat data (dari kode) ke tampilan + view binding (mengikat tampilan ke kode):

Contoh menggunakan view binding di pengontrol UI

binding.textViewUnscrambledWord.text = viewModel.currentScrambledWord

Contoh menggunakan data binding dalam file tata letak

android:text="@{gameViewModel.currentScrambledWord}"

Contoh di atas menampilkan cara menggunakan Library Data Binding untuk menetapkan data aplikasi ke tampilan/widget secara langsung dalam file tata letak. Perhatikan penggunaan sintaksis @{} dalam ekspresi penetapan.

Keuntungan utama dari penggunaan data binding adalah, Anda dapat menghapus banyak panggilan framework UI dalam aktivitas, yang menjadikannya lebih sederhana dan lebih mudah dikelola. Hal ini juga dapat meningkatkan performa aplikasi Anda dan membantu mencegah kebocoran memori dan pengecualian pointer null.

Langkah 1: Ubah view binding ke data binding

  1. Di file build.gradle(Module), aktifkan properti dataBinding pada bagian buildFeatures.

Ganti

buildFeatures {
   viewBinding = true
}

dengan

buildFeatures {
   dataBinding = true
}

Lakukan sinkronisasi gradle saat diminta oleh Android Studio.

  1. Untuk menggunakan data binding di project Kotlin, Anda harus menerapkan plugin kotlin-kapt. Langkah ini sudah dilakukan untuk Anda di file build.gradle(Module).
plugins {
   id 'com.android.application'
   id 'kotlin-android'
   id 'kotlin-kapt'
}

Langkah di atas otomatis menghasilkan class binding untuk setiap file XML tata letak dalam aplikasi. Jika nama file tata letak adalah activity_main.xml maka class autogen akan disebut ActivityMainBinding.

Langkah 2: Konversikan file tata letak menjadi tata letak data binding

File tata letak data binding sedikit berbeda dan dimulai dengan tag root dari <layout> diikuti dengan elemen <data> opsional dan elemen root view. Di elemen tampilan inilah root Anda akan berada dalam file tata letak non-binding.

  1. Buka game_fragment.xml, pilih tab code.
  2. Untuk mengonversi tata letak ke tata letak Data Binding, gabungkan elemen root dalam tag <layout>. Anda juga harus memindahkan definisi namespace (atribut yang dimulai dengan xmlns:) ke elemen root baru. Tambahkan tag <data></data> di dalam tag <layout> di atas elemen root. Android Studio menawarkan cara praktis untuk melakukannya secara otomatis: Klik kanan elemen root (ScrollView), pilih Show Context Actions > Convert to data binding layout.

8d48f58c2bdccb52.png

  1. Tata letak Anda akan terlihat seperti ini:
<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>
  1. Di GameFragment, pada awal metode onCreateView(), ubah pembuatan instance variabel binding untuk menggunakan data binding.

Ganti

binding = GameFragmentBinding.inflate(inflater, container, false)

dengan

binding = DataBindingUtil.inflate(inflater, R.layout.game_fragment, container, false)
  1. Kompilasi kodenya; seharusnya Anda dapat mengompilasi tanpa masalah apa pun. Aplikasi Anda sekarang menggunakan data binding dan tampilan di tata letak dapat mengakses data aplikasi.

8. Menambahkan variabel data binding

Dalam tugas ini, Anda akan menambahkan properti di file tata letak untuk mengakses data aplikasi dari viewModel. Anda akan melakukan inisialisasi variabel tata letak dalam kode.

  1. Di game_fragment.xml, di dalam tag <data>, tambahkan tag turunan yang disebut <variable>, deklarasikan properti yang disebut gameViewModel dan jenis GameViewModel. Anda akan menggunakannya untuk mengikat data di ViewModel ke tata letak.
<data>
   <variable
       name="gameViewModel"
       type="com.example.android.unscramble.ui.game.GameViewModel" />
</data>

Perhatikan bahwa jenis gameViewModel berisi nama paket. Pastikan nama paket ini cocok dengan nama paket di aplikasi Anda.

  1. Di bawah deklarasi gameViewModel, tambahkan variabel lain di dalam tag <data> jenis Integer, dan beri nama maxNoOfWords. Anda akan menggunakan ini untuk mengikat ke variabel di ViewModel guna menyimpan jumlah kata per game.
<data>
   ...
   <variable
       name="maxNoOfWords"
       type="int" />
</data>
  1. Pada GameFragment di awal metode onViewCreated(), inisialisasi variabel tata letak gameViewModel dan maxNoOfWords.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
   super.onViewCreated(view, savedInstanceState)

   binding.gameViewModel = viewModel

   binding.maxNoOfWords = MAX_NO_OF_WORDS
...
}
  1. LiveData dapat diobservasi oleh siklus proses, jadi Anda harus meneruskan pemilik siklus proses ke tata letak. Pada GameFragment, di dalam metode onViewCreated(), di bawah inisialisasi variabel binding, tambahkan kode berikut.
   // 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

Ingatlah bahwa Anda menerapkan fungsi serupa saat menerapkan observer LiveData. Anda meneruskan viewLifecycleOwner sebagai salah satu parameter ke observer LiveData.

9. Menggunakan ekspresi binding

Ekspresi binding ditulis dalam tata letak di properti atribut (seperti android:text) yang mereferensikan properti tata letak. Properti tata letak dideklarasikan di bagian atas file tata letak data binding, melalui tag <variable>. Saat salah satu variabel dependen berubah, ‘DB Library’ akan menjalankan ekspresi binding Anda (sehingga memperbarui tampilan). Deteksi perubahan ini adalah pengoptimalan hebat yang Anda dapatkan secara cuma-cuma, saat menggunakan Library Data Binding.

Sintaksis untuk ekspresi binding

Ekspresi binding dimulai dengan simbol @ dan diletakkan di tengah dua tanda kurung kurawal {}. Pada contoh berikut, teks TextView disetel ke properti firstName variabel user:

Contoh:

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}" />

Langkah 1: Tambahkan ekspresi binding ke kata saat ini

Pada langkah ini, Anda mengikat tampilan teks kata saat ini ke objek LiveData di ViewModel.

  1. Di game_fragment.xml, tambahkan atribut text ke tampilan teks textView_unscrambled_word. Gunakan variabel tata letak baru, gameViewModel dan tetapkan @{gameViewModel.currentScrambledWord} ke atribut text.
<TextView
   android:id="@+id/textView_unscrambled_word"
   ...
   android:text="@{gameViewModel.currentScrambledWord}"
   .../>
  1. Di GameFragment, hapus kode observer LiveData untuk currentScrambledWord: Anda tidak memerlukan kode observer dalam fragmen lagi. Tata letak ini menerima pembaruan perubahan pada LiveData secara langsung.

Hapus:

viewModel.currentScrambledWord.observe(viewLifecycleOwner,
   { newWord ->
       binding.textViewUnscrambledWord.text = newWord
   })
  1. Jalankan aplikasi Anda, aplikasi seharusnya berfungsi seperti sebelumnya. Namun, kini tampilan teks kata yang diacak menggunakan ekspresi binding untuk mengupdate UI, bukan observer LiveData.

Langkah 2: Tambahkan ekspresi binding ke skor dan jumlah kata

Resource dalam ekspresi data binding

Ekspresi data binding dapat mereferensikan resource aplikasi dengan sintaksis berikut.

Contoh:

android:padding="@{@dimen/largePadding}"

Pada contoh di atas, atribut padding diberi nilai largePadding dari file resource dimen.xml.

Anda juga dapat meneruskan properti tata letak sebagai parameter resource.

Contoh:

android:text="@{@string/example_resource(user.lastName)}"

strings.xml

<string name="example_resource">Last Name: %s</string>

Pada contoh di atas, example_resource adalah resource string dengan placeholder %s. Anda meneruskan user.lastName sebagai parameter resource dalam ekspresi binding, dengan user yang merupakan variabel tata letak.

Pada langkah ini, Anda akan menambahkan ekspresi binding ke tampilan teks skor dan jumlah kata, dengan meneruskan parameter resource. Langkah ini mirip dengan langkah yang Anda lakukan untuk textView_unscrambled_word di atas.

  1. Di game_fragment.xml, perbarui atribut text untuk tampilan teks word_count dengan ekspresi binding berikut. Gunakan resource string word_count dan masukkan dalam gameViewModel.currentWordCount, dan maxNoOfWords sebagai parameter resource.
<TextView
   android:id="@+id/word_count"
   ...
   android:text="@{@string/word_count(gameViewModel.currentWordCount, maxNoOfWords)}"
   .../>
  1. Perbarui atribut text untuk tampilan teks score dengan ekspresi binding berikut. Gunakan resource string score dan teruskan gameViewModel.score sebagai parameter resource.
<TextView
   android:id="@+id/score"
   ...
   android:text="@{@string/score(gameViewModel.score)}"
   ... />
  1. Hapus observer LiveData dari GameFragment. Anda tidak membutuhkannya lagi, ekspresi binding akan mengupdate UI saat LiveData yang sesuai berubah.

Hapus:

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)
   })
  1. Jalankan aplikasi dan mainkan beberapa kata. Sekarang kode Anda menggunakan LiveData dan ekspresi binding untuk mengupdate UI.

7880e60dc0a6f95c.png 9ef2fdf21ffa5c99.png

Selamat! Anda telah mempelajari cara menggunakan LiveData dengan observer LiveData dan LiveData dengan ekspresi binding.

10. Menguji aplikasi Unscramble dengan Talkback diaktifkan

Seperti yang telah Anda pelajari selama kursus ini, Anda ingin mem-build aplikasi yang bisa diakses oleh sebanyak mungkin pengguna. Beberapa pengguna mungkin menggunakan Talkback untuk mengakses dan menavigasi aplikasi Anda. TalkBack adalah pembaca layar Google yang disertakan di perangkat Android. TalkBack memberikan respons lisan sehingga Anda dapat menggunakan perangkat tanpa melihat layar.

Jika Talkback aktif, pastikan pemain bisa memainkan game.

  1. Aktifkan Talkback di perangkat Anda dengan mengikuti petunjuk ini.
  2. Kembali ke aplikasi Unscramble.
  3. Jelajahi aplikasi Anda dengan Talkback menggunakan petunjuk ini. Geser ke kanan untuk menavigasi elemen layar secara berurutan, dan geser ke kiri untuk beralih ke arah yang berlawanan. Ketuk dua kali di mana saja untuk memilih. Verifikasi bahwa Anda dapat menjangkau semua elemen aplikasi dengan gestur geser.
  4. Pastikan bahwa pengguna Talkback dapat menavigasi ke setiap item di layar.
  5. Perhatikan bahwa Talkback mencoba membaca kata acak sebagai kata. Hal ini dapat membingungkan pemain karena ini bukan kata sebenarnya.
  6. Pengalaman pengguna yang lebih baik adalah membuat Talkback membacakan dengan keras setiap karakter dari kata yang diacak. Dalam GameViewModel, konversikan kata acak String menjadi string Spannable. String spannable adalah string yang berisi beberapa informasi tambahan. Dalam hal ini, kita ingin mengaitkan string dengan TtsSpan dari TYPE_VERBATIM, sehingga mesin text-to-speech membaca kata acak secara jelas dan lantang, karakter demi karakter.
  7. Di GameViewModel, gunakan kode berikut untuk mengubah cara variabel currentScrambledWord dideklarasikan:
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
    }
}

Variabel ini sekarang menjadi LiveData<Spannable> bukan LiveData<String>. Anda tidak perlu khawatir jika belum memahami semua detail cara kerjanya, tetapi yang pasti penerapannya menggunakan transformasi LiveData untuk mengonversi kata acak String saat ini menjadi string Spannable yang dapat ditangani dengan tepat oleh layanan aksesibilitas. Pada codelab berikutnya, Anda akan mempelajari lebih lanjut transformasi LiveData, yang memungkinkan Anda menampilkan instance LiveData yang berbeda berdasarkan nilai LiveData yang sesuai.

  1. Jalankan aplikasi Unscramble, jelajahi aplikasi dengan Talkback. TalkBack seharusnya membacakan setiap karakter dari kata yang diacak sekarang.

Untuk informasi selengkapnya tentang cara membuat aplikasi lebih mudah diakses, lihat prinsip ini.

11. Menghapus kode yang tidak digunakan

Praktik yang baik untuk dilakukan adalah menghapus kode mati, tidak terpakai, dan tidak diinginkan untuk kode solusi. Hal ini membuat kode mudah dikelola, yang juga memudahkan rekan tim baru untuk memahami kode dengan lebih baik.

  1. Dalam GameFragment, hapus metode getNextScrambledWord() dan onDetach().
  2. Dalam GameViewModel, hapus metode onCleared().
  3. Hapus semua impor yang tidak digunakan, di bagian atas file sumber. Nantinya impor tersebut akan berwarna abu-abu.

Anda tidak memerlukan laporan log lagi. Anda dapat menghapusnya dari kode jika perlu.

  1. [Opsional] Hapus pernyataan Log dalam file sumber (GameFragment.kt dan GameViewModel.kt) yang Anda tambahkan di codelab sebelumnya, untuk memahami siklus proses ViewModel.

12. Kode solusi

Kode solusi untuk codelab ini ada dalam project yang ditampilkan di bawah.

  1. Buka halaman repositori GitHub yang disediakan untuk project.
  2. Pastikan nama cabang cocok dengan nama cabang yang ditentukan dalam codelab. Misalnya, dalam screenshot berikut, nama cabang adalah main (utama).

1e4c0d2c081a8fd2.png

  1. Di halaman GitHub project, klik tombol Code yang akan menampilkan pop-up.

1debcf330fd04c7b.png

  1. Pada pop-up, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
  2. Temukan file di komputer Anda (mungkin di folder Downloads).
  3. Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.

Membuka project di Android Studio

  1. Mulai Android Studio.
  2. Di jendela Welcome to Android Studio, klik Open.

d8e9dbdeafe9038a.png

Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > Open.

8d1fda7396afe8e5.png

  1. Di file browser, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
  2. Klik dua kali pada folder project tersebut.
  3. Tunggu Android Studio membuka project.
  4. Klik tombol Run 8de56cba7583251f.png untuk mem-build dan menjalankan aplikasi. Pastikan aplikasi di-build seperti yang diharapkan.

13. Ringkasan

  • LiveData menyimpan data; LiveData adalah wrapper yang dapat digunakan dengan data apa pun
  • LiveData dapat diamati, yang berarti bahwa observer akan diberi tahu saat data yang dimiliki objek LiveData berubah.
  • LiveData mendukung siklus proses. Saat Anda melampirkan observer ke LiveData, observer dikaitkan dengan LifecycleOwner (biasanya Activity atau Fragment). LiveData hanya mengupdate observer yang dalam status siklus proses aktif seperti STARTED atau RESUMED. Anda dapat membaca info selengkapnya tentang LiveData dan observasi di sini.
  • Aplikasi dapat memproses perubahan LiveData dari tata letak menggunakan Data Binding dan ekspresi binding.
  • Ekspresi binding ditulis dalam tata letak di properti atribut (seperti android:text) yang mereferensikan properti tata letak.

14. Mempelajari lebih lanjut

Postingan blog