Dasar-Dasar Android Kotlin 05.3: Data binding dengan ViewModel dan 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

Pada codelab sebelumnya di tutorial ini, Anda meningkatkan kode untuk aplikasi GuessTheWord. Aplikasi ini sekarang menggunakan objek ViewModel, sehingga data aplikasi bertahan dari perubahan konfigurasi perangkat seperti rotasi layar dan perubahan ketersediaan keyboard. Anda juga menambahkan LiveData yang dapat diamati, sehingga tampilan akan otomatis diberi tahu saat data yang diamati berubah.

Dalam codelab ini, Anda terus menggunakan aplikasi GuessTheWord. Anda mengikat tampilan ke class ViewModel di aplikasi sehingga tampilan di tata letak Anda berkomunikasi langsung dengan objek ViewModel. (Sampai sekarang di aplikasi Anda, tampilan telah berkomunikasi secara tidak langsung dengan ViewModel, melalui fragmen aplikasi.) Setelah mengintegrasikan data binding dengan objek ViewModel, Anda tidak lagi memerlukan pengendali klik di fragmen aplikasi, sehingga Anda menghapusnya.

Anda juga mengubah aplikasi GuessTheWord untuk menggunakan LiveData sebagai sumber data binding untuk memberi tahu UI tentang perubahan data, tanpa menggunakan metode observer LiveData.

Yang harus sudah Anda ketahui

  • Cara membuat aplikasi Android dasar di Kotlin.
  • Cara kerja siklus proses aktivitas dan fragmen.
  • Cara menggunakan objek ViewModel di aplikasi Anda.
  • Cara menyimpan data menggunakan LiveData di ViewModel.
  • Cara menambahkan metode observer untuk mengamati perubahan dalam data LiveData.

Yang akan Anda pelajari

  • Cara menggunakan elemen Library Data Binding.
  • Cara mengintegrasikan ViewModel dengan data binding.
  • Cara mengintegrasikan LiveData dengan data binding.
  • Cara menggunakan binding pemroses untuk menggantikan pemroses klik dalam fragmen.
  • Cara menambahkan pemformatan string ke ekspresi data binding.

Yang akan Anda lakukan

  • Tampilan di tata letak GuessTheWord berkomunikasi secara tidak langsung dengan objek ViewModel, menggunakan pengontrol UI (fragmen) untuk menyampaikan informasi. Dalam codelab ini, Anda mengikat tampilan aplikasi ke objek ViewModel sehingga tampilan berkomunikasi secara langsung dengan objek ViewModel.
  • Anda mengubah aplikasi untuk menggunakan LiveData sebagai sumber data binding. Setelah perubahan ini, objek LiveData akan memberi tahu UI tentang perubahan dalam data, dan metode pengamat LiveData tidak lagi diperlukan.

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 mengintegrasikan data binding dengan LiveData dalam objek ViewModel. Ini mengotomatiskan komunikasi antara tampilan dalam tata letak dan objek ViewModel, dan memungkinkan Anda menyederhanakan kode dengan menggunakan LiveData.

Layar judul

Layar game

Layar skor

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.
  3. Perhatikan bahwa tombol Oke menampilkan kata berikutnya dan meningkatkan skor sebesar satu, sedangkan tombol Lewati menampilkan kata berikutnya dan menurunkan skor sebesar satu. Tombol Akhiri Game mengakhiri game.
  4. Lihat semua kata, dan perhatikan bahwa aplikasi otomatis membuka layar skor.

Dalam codelab sebelumnya, Anda menggunakan data binding sebagai jenis yang aman untuk mengakses tampilan di aplikasi GuessTheWord. Namun, kemampuan sebenarnya dari data binding adalah dalam melakukan hal yang disarankan namanya: mengikat data secara langsung ke objek tampilan di aplikasi Anda.

Arsitektur aplikasi saat ini

Di aplikasi Anda, tampilan ditentukan dalam tata letak XML, dan data untuk tampilan tersebut disimpan dalam objek ViewModel. Di antara setiap tampilan dan ViewModel yang sesuai adalah pengontrol UI, yang berfungsi sebagai relai di antara keduanya.

Misalnya:

  • Tombol Got it didefinisikan sebagai tampilan Button dalam file tata letak game_fragment.xml.
  • Saat pengguna mengetuk tombol Got It, pemroses klik di fragmen GameFragment memanggil pemroses klik yang sesuai di GameViewModel.
  • Skor diperbarui di GameViewModel.

Tampilan Button dan GameViewModel tidak berkomunikasi langsung—keduanya memerlukan pemroses klik yang berada di GameFragment.

ViewModel yang diteruskan ke data binding

Akan lebih mudah jika tampilan dalam tata letak berkomunikasi langsung dengan data dalam objek ViewModel, tanpa mengandalkan pengontrol UI sebagai perantara.

Objek ViewModel menyimpan semua data UI di aplikasi GuessTheWord. Dengan meneruskan objek ViewModel ke data binding, Anda dapat mengotomatiskan beberapa komunikasi antara tampilan dan objek ViewModel.

Dalam tugas ini, Anda akan mengaitkan class GameViewModel dan ScoreViewModel dengan tata letak XML terkaitnya. Anda juga menyiapkan binding pemroses untuk menangani peristiwa klik.

Langkah 1: Tambahkan data binding untuk GameViewModel

Pada langkah ini, Anda akan mengaitkan GameViewModel dengan file tata letak yang sesuai, game_fragment.xml.

  1. Di file game_fragment.xml, tambahkan variabel data binding jenis GameViewModel. Jika Anda mengalami error di Android Studio, bersihkan dan build ulang project.
<layout ...>

   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  
   <androidx.constraintlayout...
  1. Di file GameFragment, teruskan GameViewModel ke data binding.

    Untuk melakukannya, tetapkan viewModel ke variabel binding.gameViewModel, yang Anda deklarasikan di langkah sebelumnya. Masukkan kode ini di dalam onCreateView(), setelah viewModel diinisialisasi. Jika Anda mengalami error di Android Studio, bersihkan dan build ulang project.
// Set the viewmodel for databinding - this allows the bound layout access 
// to all the data in the ViewModel
binding.gameViewModel = viewModel

Langkah 2: Gunakan binding pemroses untuk penanganan peristiwa

Binding pemroses adalah ekspresi binding yang berjalan saat peristiwa seperti onClick(), onZoomIn(), atau onZoomOut() dipicu. Binding pemroses ditulis sebagai ekspresi lambda.

Data binding membuat pemroses dan menetapkan pemroses pada tampilan. Saat peristiwa yang didengarkan terjadi, pemroses akan mengevaluasi ekspresi lambda. Binding pemroses berfungsi dengan Plugin Android Gradle versi 2.0 atau lebih tinggi. Untuk mempelajari lebih lanjut, baca Tata letak dan ekspresi binding.

Pada langkah ini, Anda mengganti pemroses klik di GameFragment dengan binding pemroses di file game_fragment.xml.

  1. Di game_fragment.xml, tambahkan atribut onClick ke skip_button. Tentukan ekspresi binding dan panggil metode onSkip() di GameViewModel. Ekspresi binding ini disebut binding pemroses.
<Button
   android:id="@+id/skip_button"
   ...
   android:onClick="@{() -> gameViewModel.onSkip()}"
   ... />
  1. Demikian pula, ikat peristiwa klik correct_button ke metode onCorrect() di GameViewModel.
<Button
   android:id="@+id/correct_button"
   ...
   android:onClick="@{() -> gameViewModel.onCorrect()}"
   ... />
  1. Ikat peristiwa klik end_game_button dengan metode onGameFinish() di GameViewModel.
<Button
   android:id="@+id/end_game_button"
   ...
   android:onClick="@{() -> gameViewModel.onGameFinish()}"
   ... />
  1. Di GameFragment, hapus pernyataan yang menetapkan pemroses klik, dan hapus fungsi yang dipanggil oleh pemroses klik. Anda tidak lagi memerlukannya.

Kode yang akan dihapus:

binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
binding.endGameButton.setOnClickListener { onEndGame() }

/** Methods for buttons presses **/
private fun onSkip() {
   viewModel.onSkip()
}
private fun onCorrect() {
   viewModel.onCorrect()
}
private fun onEndGame() {
   gameFinished()
}

Langkah 3: Tambahkan data binding untuk ScoreViewModel

Pada langkah ini, Anda akan mengaitkan ScoreViewModel dengan file tata letak yang sesuai, score_fragment.xml.

  1. Di file score_fragment.xml, tambahkan variabel binding jenis ScoreViewModel. Langkah ini mirip dengan langkah yang Anda lakukan untuk GameViewModel di atas.
<layout ...>
   <data>
       <variable
           name="scoreViewModel"
           type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
   </data>
   <androidx.constraintlayout.widget.ConstraintLayout
  1. Di score_fragment.xml, tambahkan atribut onClick ke play_again_button. Tentukan binding pemroses dan panggil metode onPlayAgain() di ScoreViewModel.
<Button
   android:id="@+id/play_again_button"
   ...
   android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
   ... />
  1. Di ScoreFragment, di dalam onCreateView(), lakukan inisialisasi viewModel. Lalu lakukan inisialisasi variabel binding binding.scoreViewModel.
viewModel = ...
binding.scoreViewModel = viewModel
  1. Di ScoreFragment, hapus kode yang menetapkan pemroses klik untuk playAgainButton. Jika Android Studio menampilkan error, bersihkan dan lakukan build ulang pada project.

Kode yang akan dihapus:

binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. Jalankan aplikasi Anda. Aplikasi seharusnya berfungsi seperti sebelumnya, tetapi sekarang tampilan tombol berkomunikasi langsung dengan objek ViewModel. Tampilan tidak lagi berkomunikasi melalui pengendali klik tombol di ScoreFragment.

Memecahkan masalah pesan error data binding

Jika aplikasi menggunakan data binding, proses kompilasi akan menghasilkan class perantara yang digunakan untuk data binding. Aplikasi dapat berisi error yang tidak terdeteksi oleh Android Studio hingga Anda mencoba mengompilasi aplikasi, sehingga Anda tidak melihat peringatan atau kode merah saat menulis kode. Namun pada waktu kompilasi, Anda akan mendapatkan error samar yang berasal dari class perantara yang dihasilkan.

Jika Anda mendapatkan pesan error samar:

  1. Perhatikan dengan cermat pesan di panel Build Android Studio. Jika Anda melihat lokasi yang berakhiran databinding, berarti terjadi error dengan data binding.
  2. Pada file XML tata letak, periksa error pada atribut onClick yang menggunakan data binding. Cari fungsi yang dipanggil ekspresi lambda, dan pastikan ekspresi tersebut ada.
  3. Di bagian <data> pada XML, periksa ejaan variabel data binding.

Misalnya, perhatikan kesalahan ejaan nama fungsi onCorrect() dalam nilai atribut berikut:

android:onClick="@{() -> gameViewModel.onCorrectx()}"

Perhatikan juga kesalahan ejaan gameViewModel di bagian <data> file XML:

<data>
   <variable
       name="gameViewModelx"
       type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>

Android Studio tidak mendeteksi error seperti ini hingga Anda mengompilasi aplikasi, lalu compiler menampilkan pesan error seperti berikut:

error: cannot find symbol
import com.example.android.guesstheword.databinding.GameFragmentBindingImpl"

symbol:   class GameFragmentBindingImpl
location: package com.example.android.guesstheword.databinding

Data binding berfungsi baik dengan LiveData yang digunakan dengan objek ViewModel. Setelah menambahkan data binding ke objek ViewModel, Anda siap menggabungkan LiveData.

Dalam tugas ini, Anda mengubah aplikasi GuessTheWord untuk menggunakan LiveData sebagai sumber data binding untuk memberi tahu UI tentang perubahan data, tanpa menggunakan metode observer LiveData.

Langkah 1: Tambahkan kata LiveData ke file game_fragment.xml

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

  1. Di game_fragment.xml, tambahkan atribut android:text ke tampilan teks word_text.

Tetapkan variabel tersebut ke objek LiveData, word dari GameViewModel, menggunakan variabel binding, gameViewModel.

<TextView
   android:id="@+id/word_text"
   ...
   android:text="@{gameViewModel.word}"
   ... />

Perhatikan bahwa Anda tidak perlu menggunakan word.value. Sebagai gantinya, Anda dapat menggunakan objek LiveData sebenarnya. Objek LiveData menampilkan nilai word saat ini. Jika nilai word null, objek LiveData akan menampilkan string kosong.

  1. Di GameFragment, di onCreateView(), setelah menginisialisasi gameViewModel, tetapkan aktivitas saat ini sebagai pemilik siklus proses variabel binding. Ini menentukan cakupan objek LiveData di atas, yang memungkinkan objek untuk otomatis memperbarui tampilan dalam tata letak, game_fragment.xml.
binding.gameViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
  1. Di GameFragment, hapus observer untuk LiveData word.

Kode yang akan dihapus:

/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
   binding.wordText.text = newWord
})
  1. Jalankan aplikasi dan mainkan game. Sekarang kata saat ini sedang diperbarui tanpa metode pengamat di pengontrol UI.

Langkah 2: Tambahkan skor LiveData ke file score_fragment.xml

Pada langkah ini, Anda mengikat LiveData score ke tampilan teks skor di fragmen skor.

  1. Di score_fragment.xml, tambahkan atribut android:text ke tampilan teks skor. Tetapkan scoreViewModel.score ke atribut text. Karena score adalah bilangan bulat, konversikan menjadi string menggunakan String.valueOf().
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{String.valueOf(scoreViewModel.score)}"
   ... />
  1. Di ScoreFragment, setelah menginisialisasi scoreViewModel, tetapkan aktivitas saat ini sebagai pemilik siklus proses variabel binding.
binding.scoreViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
  1. Di ScoreFragment, hapus observer untuk objek score.

Kode yang akan dihapus:

// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Jalankan aplikasi dan mainkan game. Perhatikan bahwa skor di fragmen skor ditampilkan dengan benar, tanpa pengamat di fragmen skor.

Langkah 3: Tambahkan pemformatan string dengan data binding

Di tata letak, Anda dapat menambahkan pemformatan string bersama dengan data binding. Dalam tugas ini, Anda memformat kata saat ini untuk menambahkan tanda kutip di sekitarnya. Anda juga dapat memformat string skor sebagai awalan Skor Saat Ini, seperti yang ditunjukkan pada gambar berikut.

  1. Di string.xml, tambahkan string berikut, yang akan Anda gunakan untuk memformat tampilan teks word dan score. %s dan %d adalah placeholder untuk kata dan skor saat ini.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
  1. Di game_fragment.xml, perbarui atribut text dari tampilan teks word_text untuk menggunakan resource string quote_format. Lewati gameViewModel.word. Ini meneruskan kata saat ini sebagai argumen ke string pemformatan.
<TextView
   android:id="@+id/word_text"
   ...
   android:text="@{@string/quote_format(gameViewModel.word)}"
   ... />
  1. Format tampilan teks score yang mirip dengan word_text. Di game_fragment.xml, tambahkan atribut text ke tampilan teks score_text. Gunakan resource string score_format, yang menggunakan satu argumen numerik, yang diwakili oleh placeholder %d. Teruskan objek LiveData, score, sebagai argumen ke string pemformatan ini.
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{@string/score_format(gameViewModel.score)}"
   ... />
  1. Di class GameFragment, di dalam metode onCreateView(), hapus kode observer score.

Kode yang akan dihapus:

viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Bersihkan, build ulang, dan jalankan aplikasi Anda, lalu mainkan game. Perhatikan bahwa kata saat ini dan skor diformat di layar game.

Selamat! Anda telah mengintegrasikan LiveData dan ViewModel dengan data binding di aplikasi Anda. Ini memungkinkan tampilan pada tata letak berkomunikasi langsung dengan ViewModel, tanpa menggunakan pengendali klik dalam fragmen. Anda juga telah menggunakan objek LiveData sebagai sumber data binding untuk otomatis memberi tahu UI tentang perubahan data, tanpa metode observer LiveData.

Project Android Studio: GuessTheWord

  • Library Data Binding berfungsi sempurna dengan Komponen Arsitektur Android seperti ViewModel dan LiveData.
  • Tata letak di aplikasi Anda dapat diikat ke data di Komponen Arsitektur, yang sudah membantu Anda mengelola siklus proses pengontrol UI dan memberi tahu tentang perubahan dalam data.

Data binding ViewModel

  • Anda dapat mengaitkan ViewModel dengan tata letak menggunakan data binding.
  • Objek ViewModel menyimpan data UI. Dengan meneruskan objek ViewModel ke data binding, Anda dapat mengotomatiskan beberapa komunikasi antara tampilan dan objek ViewModel.

Cara mengaitkan ViewModel dengan tata letak:

  • Di file tata letak, tambahkan variabel data binding jenis ViewModel.
   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  • Di file GameFragment, teruskan GameViewModel ke data binding.
binding.gameViewModel = viewModel

Binding pemroses

  • Binding pemroses adalah ekspresi binding di tata letak yang berjalan saat peristiwa klik seperti onClick() dipicu.
  • Binding pemroses ditulis sebagai ekspresi lambda.
  • Dengan menggunakan binding pemroses, Anda mengganti pemroses klik di pengontrol UI dengan binding pemroses di file tata letak.
  • Data binding membuat pemroses dan menetapkan pemroses pada tampilan.
 android:onClick="@{() -> gameViewModel.onSkip()}"

Menambahkan LiveData ke data binding

  • Objek LiveData dapat digunakan sebagai sumber data binding untuk otomatis memberi tahu UI tentang perubahan data.
  • Anda dapat mengikat tampilan langsung ke objek LiveData di ViewModel. Jika LiveData di ViewModel berubah, tampilan di tata letak dapat diupdate secara otomatis, tanpa metode pengamat di pengontrol UI.
android:text="@{gameViewModel.word}"
  • Agar data binding LiveData berfungsi, tetapkan aktivitas saat ini (pengontrol UI) sebagai pemilik siklus proses variabel binding di pengontrol UI.
binding.lifecycleOwner = this

Pemformatan string dengan data binding

  • Dengan menggunakan data binding, Anda dapat memformat resource string dengan placeholder seperti %s untuk string dan %d untuk bilangan bulat.
  • Untuk memperbarui atribut text tampilan, teruskan objek LiveData sebagai argumen ke string pemformatan.
 android:text="@{@string/quote_format(gameViewModel.word)}"

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

Manakah dari pernyataan berikut yang tidak benar tentang binding pemroses?

  • Binding pemroses adalah ekspresi binding yang berjalan saat peristiwa terjadi.
  • Binding pemroses berfungsi dengan semua versi plugin Android Gradle.
  • Binding pemroses ditulis sebagai ekspresi lambda.
  • Binding pemroses mirip dengan referensi metode, tetapi memungkinkan Anda menjalankan ekspresi data binding arbitrer.

Pertanyaan 2

Anggap aplikasi Anda menyertakan resource string ini:
<string name="generic_name">Hello %s</string>

Manakah dari hal berikut merupakan sintaksis yang benar untuk pemformatan string, menggunakan ekspresi data binding?

  • android:text= "@{@string/generic_name(user.name)}"
  • android:text= "@{string/generic_name(user.name)}"
  • android:text= "@{@generic_name(user.name)}"
  • android:text= "@{@string/generic_name,user.name}"

Pertanyaan 3

Kapan ekspresi binding pemroses dievaluasi dan dijalankan?

  • Saat data yang disimpan oleh LiveData diubah
  • Saat aktivitas dibuat ulang karena perubahan konfigurasi
  • Saat peristiwa seperti onClick() terjadi
  • Saat aktivitas berpindah ke latar belakang

Mulai tutorial berikutnya: 5.4: Transformasi LiveData

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