Android Kotlin Fundamentals 05.3: การเชื่อมโยงข้อมูลกับ Viewmodel และ LiveData

Codelab นี้เป็นส่วนหนึ่งของหลักสูตรพื้นฐานเกี่ยวกับ Kotlin ใน Android คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้ หากทํางานผ่าน Codelab ตามลําดับ Codelab ของหลักสูตรทั้งหมดจะแสดงอยู่ในหน้า Landing Page ของ Codelab ของ Android Kotlin Fundamentals

บทนำ

ใน Codelab ก่อนหน้าในบทเรียนนี้ คุณได้ปรับปรุงโค้ดของแอป GuessTheWord แล้ว ตอนนี้แอปใช้ออบเจ็กต์ ViewModel ดังนั้นข้อมูลแอปจึงมีผลต่อการเปลี่ยนแปลงการกําหนดค่าอุปกรณ์ เช่น การหมุนหน้าจอและการเปลี่ยนแปลงของความพร้อมใช้งานของแป้นพิมพ์ คุณเพิ่ม LiveData ที่สังเกตได้ด้วย เพื่อให้การแจ้งเตือนได้รับการแจ้งเตือนโดยอัตโนมัติเมื่อข้อมูลที่มีการเปลี่ยนแปลงมีการเปลี่ยนแปลง

ใน Codelab นี้ คุณจะทํางานกับแอป GuessTheWord ต่อไป คุณจะเชื่อมโยงมุมมองกับคลาส ViewModel ในแอปเพื่อให้มุมมองในเลย์เอาต์สื่อสารกับออบเจ็กต์ ViewModel ได้โดยตรง (ที่ผ่านมาจนถึงตอนนี้แอป ข้อมูลพร็อพเพอร์ตี้ได้สื่อสารกับทางอ้อมกับ ViewModel ตามส่วนย่อยของแอป) หลังจากผสานรวมการเชื่อมโยงข้อมูลกับออบเจ็กต์ ViewModel แล้ว คุณไม่จําเป็นต้องใช้เครื่องจัดการการคลิกใน Fragment ของแอปอีกต่อไป คุณจึงนําออกได้

นอกจากนี้คุณยังเปลี่ยนแอป GuessTheWord เพื่อใช้ LiveData เป็นแหล่งข้อมูลแบบผูกข้อมูลเพื่อแจ้ง UI เกี่ยวกับการเปลี่ยนแปลงในข้อมูลโดยไม่ต้องใช้เมธอดการสังเกตการณ์ LiveData รายการ

สิ่งที่ควรทราบอยู่แล้ว

  • วิธีสร้างแอป Android ขั้นพื้นฐานใน Kotlin
  • วิธีการทํางานของวงจรกิจกรรมและส่วนย่อย
  • วิธีใช้ออบเจ็กต์ ViewModel ในแอป
  • วิธีเก็บข้อมูลโดยใช้ LiveData ใน ViewModel
  • วิธีเพิ่มวิธีการสังเกตการณ์เพื่อดูการเปลี่ยนแปลงในข้อมูล LiveData

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้องค์ประกอบของไลบรารีการเชื่อมโยงข้อมูล
  • วิธีผสานรวม ViewModel กับการเชื่อมโยงข้อมูล
  • วิธีผสานรวม LiveData กับการเชื่อมโยงข้อมูล
  • วิธีใช้การเชื่อมโยง Listener เพื่อแทนที่ Listener การคลิกในส่วนย่อย
  • วิธีเพิ่มการจัดรูปแบบสตริงในนิพจน์การเชื่อมโยงข้อมูล

สิ่งที่คุณจะทํา

  • มุมมองในเลย์เอาต์ GuessTheWord จะสื่อสารกับออบเจ็กต์ ViewModel ทางอ้อมโดยใช้ตัวควบคุม UI (ส่วนย่อย) เพื่อส่งต่อข้อมูล ใน Codelab นี้ คุณสามารถเชื่อมโยงมุมมองของแอปกับออบเจ็กต์ ViewModel เพื่อให้มุมมองสื่อสารกับออบเจ็กต์ ViewModel โดยตรง
  • คุณเปลี่ยนแอปให้ใช้ LiveData เป็นแหล่งที่มาของการเชื่อมโยงข้อมูล หลังจากการเปลี่ยนแปลงนี้ ออบเจ็กต์ LiveData จะแจ้ง UI เกี่ยวกับการเปลี่ยนแปลงในข้อมูล และไม่จําเป็นต้องใช้เมธอดการสังเกตการณ์ LiveData อีกต่อไป

ใน Codelab บทที่ 5 คุณจะพัฒนาแอป GuessTheWord ได้โดยเริ่มต้นด้วยโค้ดเริ่มต้น GuessTheWord เป็นเกมสไตล์ชาร์ดแบบผู้เล่น 2 คน ซึ่งผู้เล่นจะทํางานร่วมกันเพื่อให้ได้คะแนนสูงสุดเท่าที่จะเป็นไปได้

ผู้เล่นคนแรกจะดูคําในแอปและกระทําทีละคํา และอย่าแสดงคํานั้นให้ผู้เล่นคนที่สองเห็น ผู้เล่นรายที่ 2 พยายามเดาคํานั้น

ในการเล่นเกม ผู้เล่นคนแรกจะเปิดแอปในอุปกรณ์และจะเห็นคํา เช่น "guitar," ดังที่แสดงในภาพหน้าจอด้านล่าง

ผู้เล่นคนแรกจะทําตามคําและระวังอย่าพูดคํานั้นจริงๆ

  • เมื่อผู้เล่นคนที่ 2 เดาคําได้อย่างถูกต้อง ผู้เล่นคนแรกจะกดปุ่มรับทราบ ซึ่งจะเพิ่มจํานวนเป็น 1 และแสดงคําถัดไป
  • หากผู้เล่นรายที่ 2 เดาคําไม่ได้ ผู้เล่นคนแรกจะกดปุ่มข้าม ซึ่งจะลดจํานวนลงและข้ามไปยังคําถัดไป
  • หากต้องการจบเกม ให้กดปุ่มจบเกม (ฟังก์ชันนี้จะไม่อยู่ในโค้ดเริ่มต้นสําหรับ Codelab แรกในชุด)

ใน Codelab นี้ คุณสามารถปรับปรุงแอป GuessTheWord ได้โดยการผสานรวมการเชื่อมโยงข้อมูลกับ LiveData ในออบเจ็กต์ ViewModel วิธีนี้จะช่วยให้การสื่อสารระหว่างมุมมองในเลย์เอาต์และออบเจ็กต์ ViewModel เป็นไปโดยอัตโนมัติ และให้คุณทําให้โค้ดง่ายขึ้นโดยใช้ LiveData

หน้าจอชื่อ

หน้าจอเกม

หน้าจอคะแนน

ในงานนี้ คุณจะค้นหาและเรียกใช้โค้ดเริ่มต้นสําหรับ Codelab นี้ คุณจะใช้แอป GuessTheWord ที่สร้างไว้ใน Codelab ก่อนหน้าเป็นโค้ดเริ่มต้น หรือจะดาวน์โหลดแอปเริ่มต้นก็ได้

  1. (ไม่บังคับ) หากคุณไม่ได้ใช้โค้ดจาก Codelab ก่อนหน้า ให้ดาวน์โหลดโค้ดเริ่มต้นสําหรับ Codelab นี้ แตกไฟล์โค้ดแล้วเปิดโปรเจ็กต์ใน Android Studio
  2. เรียกใช้แอปและเล่นเกม
  3. โปรดสังเกตว่าปุ่มรับทราบจะแสดงคําถัดไปและเพิ่มคะแนนเป็น 1 รายการ ขณะที่ปุ่มข้ามแสดงคําถัดไปและลดคะแนนลง 1 รายการ ปุ่มสิ้นสุดเกมจะจบเกม
  4. เลื่อนดูคําทั้งหมด แล้วสังเกตว่าแอปไปยังหน้าจอคะแนนโดยอัตโนมัติ

ใน Codelab ที่ผ่านมา คุณได้ใช้การเชื่อมโยงข้อมูลเป็นวิธีที่ปลอดภัยในการเข้าถึงข้อมูลพร็อพเพอร์ตี้ในแอป GuessTheWord แต่ความสามารถที่แท้จริงของการเชื่อมโยงข้อมูลคือการดําเนินการตามชื่อ ซึ่งก็คือการเชื่อมโยงข้อมูลโดยตรงกับออบเจ็กต์ข้อมูลพร็อพเพอร์ตี้ในแอป

สถาปัตยกรรมแอปปัจจุบัน

ในแอป ระบบจะกําหนดข้อมูลพร็อพเพอร์ตี้ในเลย์เอาต์ XML และข้อมูลของข้อมูลพร็อพเพอร์ตี้เหล่านั้นจะอยู่ในออบเจ็กต์ ViewModel ระหว่างข้อมูลพร็อพเพอร์ตี้แต่ละรายการกับ ViewModel ที่เกี่ยวข้อง เป็นตัวควบคุม UI ซึ่งทําหน้าที่ส่งต่อข้อมูลระหว่างแต่ละรายการ

เช่น

  • ปุ่มรับทราบหมายถึงมุมมอง Button ในไฟล์เลย์เอาต์ game_fragment.xml
  • เมื่อผู้ใช้แตะปุ่มรับทราบ Listener การคลิกในส่วน GameFragment จะเรียก Listener การคลิกที่เกี่ยวข้องใน GameViewModel
  • คะแนนจะอัปเดตใน GameViewModel

มุมมอง Button และ GameViewModel ไม่ได้สื่อสารกันโดยตรง เพราะคุณต้องใช้ Listener การคลิกใน GameFragment

Viewmodel ที่ส่งผ่านการเชื่อมโยงข้อมูลแล้ว

ทําได้ง่ายขึ้นหากมุมมองในเลย์เอาต์สื่อสารกับข้อมูลโดยตรงในออบเจ็กต์ ViewModel โดยไม่ต้องอาศัยตัวควบคุม UI เป็นตัวกลาง

ออบเจ็กต์ ViewModel จะเก็บข้อมูล UI ทั้งหมดในแอป GuessTheWord เมื่อส่งออบเจ็กต์ ViewModel ไปยังการเชื่อมโยงข้อมูล คุณจะทําให้การสื่อสารบางอย่างระหว่างข้อมูลพร็อพเพอร์ตี้และออบเจ็กต์ ViewModel เป็นแบบอัตโนมัติได้

ในงานนี้ คุณจะได้เชื่อมโยงชั้นเรียน GameViewModel และ ScoreViewModel กับเลย์เอาต์ XML ที่เกี่ยวข้อง รวมถึงตั้งค่าการเชื่อมโยง Listener เพื่อจัดการเหตุการณ์การคลิกด้วย

ขั้นตอนที่ 1: เพิ่มการเชื่อมโยงข้อมูลสําหรับ GameViewmodel

ในขั้นตอนนี้ คุณจะเชื่อมโยง GameViewModel กับไฟล์เลย์เอาต์ที่เกี่ยวข้อง game_fragment.xml

  1. ในไฟล์ game_fragment.xml ให้เพิ่มตัวแปรการเชื่อมโยงของประเภท GameViewModel หากมีข้อผิดพลาดใน Android Studio ให้ทําความสะอาดและสร้างโปรเจ็กต์ใหม่
<layout ...>

   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  
   <androidx.constraintlayout...
  1. ในไฟล์ GameFragment ให้ส่ง GameViewModel ไปยังการเชื่อมโยงข้อมูล

    โดยกําหนด viewModel ให้กับตัวแปร binding.gameViewModel ที่คุณประกาศในขั้นตอนก่อนหน้า วางโค้ดนี้ไว้ภายใน onCreateView() หลังจากเริ่มใช้ viewModel หากมีข้อผิดพลาดใน Android Studio ให้ทําความสะอาดและสร้างโปรเจ็กต์ใหม่
// Set the viewmodel for databinding - this allows the bound layout access 
// to all the data in the ViewModel
binding.gameViewModel = viewModel

ขั้นตอนที่ 2: ใช้การเชื่อมโยง Listener สําหรับการจัดการเหตุการณ์

การเชื่อมโยง Listener คือนิพจน์การเชื่อมโยงที่จะทํางานเมื่อมีการทริกเกอร์เหตุการณ์ เช่น onClick(), onZoomIn() หรือ onZoomOut() การเชื่อมโยง Listener จะถูกเขียนเป็นนิพจน์แลมบ์ดา

การเชื่อมโยงข้อมูลจะสร้าง Listener และตั้งค่า Listener ในข้อมูลพร็อพเพอร์ตี้ เมื่อมีเหตุการณ์ Listener สําหรับ Listener จะประเมินนิพจน์แลมบ์ดา Listener การเชื่อมโยงใช้งานได้กับปลั๊กอิน Android Gradle เวอร์ชัน 2.0 ขึ้นไป หากต้องการดูข้อมูลเพิ่มเติม โปรดอ่านเลย์เอาต์และนิพจน์การเชื่อมโยง

ในขั้นตอนนี้ คุณจะแทนที่ Listener การคลิกในGameFragmentด้วยการเชื่อมโยง Listener ในไฟล์ game_fragment.xml

  1. ใน game_fragment.xml ให้เพิ่มแอตทริบิวต์ onClick ลงใน skip_button กําหนดนิพจน์การเชื่อมโยงและเรียกเมธอด onSkip() ใน GameViewModel นิพจน์การเชื่อมโยงนี้เรียกว่าการเชื่อมโยงผู้ฟัง
<Button
   android:id="@+id/skip_button"
   ...
   android:onClick="@{() -> gameViewModel.onSkip()}"
   ... />
  1. ในทํานองเดียวกัน ให้เชื่อมโยงเหตุการณ์การคลิก correct_button กับเมธอด onCorrect() ใน GameViewModel
<Button
   android:id="@+id/correct_button"
   ...
   android:onClick="@{() -> gameViewModel.onCorrect()}"
   ... />
  1. เชื่อมโยงเหตุการณ์การคลิกของ end_game_button กับเมธอด onGameFinish() ใน GameViewModel
<Button
   android:id="@+id/end_game_button"
   ...
   android:onClick="@{() -> gameViewModel.onGameFinish()}"
   ... />
  1. ใน GameFragment ให้นําคําสั่งที่กําหนด Listener การคลิกออก และนําฟังก์ชันที่ Listener การคลิกออก คุณไม่จําเป็นต้องใช้อีก

รหัสที่จะนําออก:

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()
}

ขั้นตอนที่ 3: เพิ่มการเชื่อมโยงข้อมูลสําหรับ ScoreViewModel

ในขั้นตอนนี้ คุณจะเชื่อมโยง ScoreViewModel กับไฟล์เลย์เอาต์ที่เกี่ยวข้อง score_fragment.xml

  1. ในไฟล์ score_fragment.xml ให้เพิ่มตัวแปรการเชื่อมโยงของประเภท ScoreViewModel ขั้นตอนนี้คล้ายกับสิ่งที่คุณทําสําหรับ GameViewModel ด้านบน
<layout ...>
   <data>
       <variable
           name="scoreViewModel"
           type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
   </data>
   <androidx.constraintlayout.widget.ConstraintLayout
  1. ใน score_fragment.xml ให้เพิ่มแอตทริบิวต์ onClick ลงใน play_again_button กําหนดการเชื่อมโยง Listener และเรียกเมธอด onPlayAgain() ใน ScoreViewModel
<Button
   android:id="@+id/play_again_button"
   ...
   android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
   ... />
  1. ใน ScoreFragment ให้เริ่มต้น viewModel ภายใน onCreateView() จากนั้นเริ่มต้นตัวแปรการเชื่อมโยง binding.scoreViewModel
viewModel = ...
binding.scoreViewModel = viewModel
  1. ใน ScoreFragment ให้นําโค้ดที่ตั้งค่า Listener การคลิกสําหรับ playAgainButton ออก หาก Android Studio แสดงข้อผิดพลาด โปรดทําความสะอาดและสร้างโปรเจ็กต์ใหม่

รหัสที่จะนําออก:

binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. เรียกใช้แอป แอปควรจะทํางานเหมือนก่อนหน้านี้ แต่ตอนนี้มุมมองปุ่มจะสื่อสารกับออบเจ็กต์ ViewModel โดยตรง มุมมองไม่สื่อสารผ่านเครื่องจัดการปุ่มใน ScoreFragment แล้ว

การแก้ปัญหาข้อความแสดงข้อผิดพลาดการเชื่อมโยงข้อมูล

เมื่อแอปใช้การเชื่อมโยงข้อมูล กระบวนการคอมไพล์จะสร้างคลาสระดับกลางที่ใช้สําหรับการเชื่อมโยงข้อมูล แอปอาจมีข้อผิดพลาดที่ Android Studio ไม่ตรวจพบจนกว่าคุณจะพยายามคอมไพล์แอป คุณจึงไม่เห็นคําเตือนหรือโค้ดสีแดงขณะที่เขียนโค้ด แต่เมื่อคอมไพล์แล้ว คุณจะได้รับข้อผิดพลาดเกี่ยวกับการเข้ารหัสลับที่มาจากชั้นเรียนระดับกลางที่สร้างขึ้น

หากได้รับข้อความแสดงข้อผิดพลาดที่เข้ารหัส ให้ทําดังนี้

  1. พิจารณาข้อความในช่องสร้างของ Android Studio อย่างละเอียด หากเห็นสถานที่ที่ลงท้ายด้วย databinding แสดงว่าเกิดข้อผิดพลาดในการเชื่อมโยงข้อมูล
  2. ในไฟล์ XML ของเลย์เอาต์ ให้ตรวจหาข้อผิดพลาดในแอตทริบิวต์ onClick ที่ใช้การเชื่อมโยงข้อมูล ค้นหาฟังก์ชันที่นิพจน์แลมบ์ดาเรียกใช้ แล้วตรวจสอบว่ามีฟังก์ชันนั้นอยู่
  3. ในส่วน <data> ของ XML ให้ตรวจสอบการสะกดคําของตัวแปรการเชื่อมโยงข้อมูล

เช่น โปรดสะกดชื่อฟังก์ชัน onCorrect() ที่สะกดผิดในค่าแอตทริบิวต์ต่อไปนี้

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

นอกจากนี้ โปรดสังเกตการสะกดคําผิดของ gameViewModel ในส่วน <data> ของไฟล์ XML ดังนี้

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

Android Studio ไม่ตรวจหาข้อผิดพลาดลักษณะนี้จนกว่าคุณจะคอมไพล์แอป จากนั้นคอมไพเลอร์จะแสดงข้อความแสดงข้อผิดพลาดต่อไปนี้

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

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

การเชื่อมโยงข้อมูลทํางานได้ดีกับ LiveData ที่ใช้กับออบเจ็กต์ ViewModel เมื่อเพิ่มการเชื่อมโยงข้อมูลไปยังออบเจ็กต์ ViewModel แล้ว คุณก็พร้อมที่จะรวม LiveData แล้ว

ในงานนี้ คุณจะต้องเปลี่ยนแอป GuessTheWord เพื่อใช้ LiveData เป็นแหล่งข้อมูลแบบผูกข้อมูลเพื่อแจ้ง UI เกี่ยวกับการเปลี่ยนแปลงในข้อมูลโดยไม่ต้องใช้เมธอดการสังเกตการณ์ LiveData

ขั้นตอนที่ 1: เพิ่มคําว่า LiveData ลงในไฟล์ Game_fragment.xml

ในขั้นตอนนี้ คุณจะเชื่อมโยงมุมมองข้อความปัจจุบันกับออบเจ็กต์ LiveData ใน ViewModel โดยตรง

  1. ใน game_fragment.xml ให้เพิ่มแอตทริบิวต์ android:text ลงในมุมมองข้อความ word_text

ตั้งค่าเป็นออบเจ็กต์ LiveData คือ word จาก GameViewModel โดยใช้ตัวแปรการเชื่อมโยง gameViewModel

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

สังเกตเห็นว่าคุณไม่จําเป็นต้องใช้ word.value แต่คุณใช้ออบเจ็กต์ LiveData จริงแทนได้ ออบเจ็กต์ LiveData จะแสดงค่าปัจจุบันของ word หากค่า word เป็น Null ออบเจ็กต์ LiveData จะแสดงสตริงที่ว่างเปล่า

  1. ใน GameFragment ใน onCreateView() หลังจากเริ่มต้น gameViewModel ให้ตั้งค่ากิจกรรมปัจจุบันเป็นเจ้าของวงจรของตัวแปร binding การดําเนินการนี้จะกําหนดขอบเขตของออบเจ็กต์ LiveData ด้านบน ทําให้ออบเจ็กต์สามารถอัปเดตมุมมองในเลย์เอาต์โดยอัตโนมัติได้ 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. ใน GameFragment ให้นําผู้สังเกตการณ์สําหรับ word ของ LiveData ออก

รหัสที่จะนําออก:

/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
   binding.wordText.text = newWord
})
  1. เรียกใช้แอปและเล่นเกม ตอนนี้กําลังอัปเดตคําปัจจุบันโดยไม่มีเมธอดการสังเกตการณ์ในตัวควบคุม UI

ขั้นตอนที่ 2: เพิ่มคะแนน LiveData ไปยังไฟล์ score_fragment.xml

ในขั้นตอนนี้ คุณจะเชื่อมโยง LiveData score เข้ากับมุมมองข้อความคะแนนใน Fragment คะแนน

  1. ใน score_fragment.xml ให้เพิ่มแอตทริบิวต์ android:text ลงในมุมมองข้อความคะแนน มอบหมาย scoreViewModel.score ให้กับแอตทริบิวต์ text เนื่องจาก score เป็นจํานวนเต็ม โปรดแปลงเป็นสตริงโดยใช้ String.valueOf()
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{String.valueOf(scoreViewModel.score)}"
   ... />
  1. ใน ScoreFragment หลังจากเริ่มต้น scoreViewModel ให้ตั้งค่ากิจกรรมปัจจุบันเป็นเจ้าของวงจรของตัวแปร 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. ใน ScoreFragment ให้นําการสังเกตการณ์ออกสําหรับออบเจ็กต์ score

รหัสที่จะนําออก:

// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. เรียกใช้แอปและเล่นเกม โปรดสังเกตว่าคะแนนใน Fragment คะแนนจะแสดงอย่างถูกต้อง โดยไม่มีข้อสังเกตในส่วนคะแนน

ขั้นตอนที่ 3: เพิ่มการจัดรูปแบบสตริงด้วยการเชื่อมโยงข้อมูล

ในเลย์เอาต์ คุณสามารถเพิ่มการจัดรูปแบบสตริงควบคู่ไปกับการเชื่อมโยงข้อมูลได้ ในงานนี้ คุณจะต้องจัดรูปแบบคําปัจจุบันเพื่อเพิ่มเครื่องหมายคําพูด คุณยังจัดรูปแบบสตริงคะแนนนําหน้าคะแนนปัจจุบันได้อีกด้วย ดังที่แสดงในรูปภาพต่อไปนี้

  1. ใน string.xml ให้เพิ่มสตริงต่อไปนี้ ซึ่งจะใช้ในการจัดรูปแบบมุมมองข้อความ word และ score %s และ %d เป็นตัวยึดตําแหน่งสําหรับคําปัจจุบันและคะแนนปัจจุบัน
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
  1. ใน game_fragment.xml ให้อัปเดตแอตทริบิวต์ text ของมุมมองข้อความ word_text เพื่อใช้ทรัพยากรสตริง quote_format ผ่านใน gameViewModel.word ซึ่งจะส่งผ่านคําปัจจุบันเป็นอาร์กิวเมนต์ไปยังสตริงการจัดรูปแบบ
<TextView
   android:id="@+id/word_text"
   ...
   android:text="@{@string/quote_format(gameViewModel.word)}"
   ... />
  1. จัดรูปแบบมุมมองข้อความ score ที่คล้ายกับ word_text ใน game_fragment.xml ให้เพิ่มแอตทริบิวต์ text ในมุมมองข้อความ score_text ใช้ทรัพยากรสตริง score_format ซึ่งใช้อาร์กิวเมนต์ตัวเลข 1 ค่าแทนด้วยตัวยึดตําแหน่ง %d ส่งผ่านในออบเจ็กต์ LiveData คือ score เป็นอาร์กิวเมนต์ในสตริงการจัดรูปแบบนี้
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{@string/score_format(gameViewModel.score)}"
   ... />
  1. ใน GameFragment คลาส ให้ลบโค้ดการสังเกตการณ์ score ภายในเมธอด onCreateView()

รหัสที่จะนําออก:

viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. ทําความสะอาด สร้างใหม่ และเรียกใช้แอป แล้วเล่นเกม โปรดสังเกตว่าคําปัจจุบันและคะแนนอยู่ในรูปแบบในหน้าจอเกม

ยินดีด้วย คุณได้รวม LiveData และ ViewModel เข้ากับการเชื่อมโยงข้อมูลในแอป วิธีนี้จะช่วยให้มุมมองในเลย์เอาต์สื่อสารกับ ViewModel ได้โดยตรงโดยไม่ต้องใช้เครื่องจัดการคลิกในส่วนย่อย นอกจากนี้คุณยังใช้ออบเจ็กต์ LiveData เป็นแหล่งที่มาของการเชื่อมโยงข้อมูลเพื่อแจ้ง UI โดยอัตโนมัติเกี่ยวกับการเปลี่ยนแปลงข้อมูล โดยไม่ต้องใช้เมธอดการสังเกตการณ์ LiveData รายการ

โปรเจ็กต์ Android Studio: GuessTheWord

  • ไลบรารีการเชื่อมโยงข้อมูลจะทํางานร่วมกับคอมโพเนนต์สถาปัตยกรรม Android เช่น ViewModel และ LiveData ได้อย่างราบรื่น
  • เลย์เอาต์ในแอปสามารถเชื่อมโยงกับข้อมูลในคอมโพเนนต์สถาปัตยกรรม ซึ่งช่วยให้คุณจัดการวงจรของตัวควบคุม UI และแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงในข้อมูลได้

การเชื่อมโยงข้อมูลพร็อพเพอร์ตี้ Model

  • คุณเชื่อมโยง ViewModel กับเลย์เอาต์ได้โดยใช้การเชื่อมโยงข้อมูล
  • ออบเจ็กต์ ViewModel มีข้อมูล UI อยู่ การส่งออบเจ็กต์ ViewModel ไปยังการเชื่อมโยงข้อมูลจะทําให้การสื่อสารบางอย่างระหว่างข้อมูลพร็อพเพอร์ตี้และออบเจ็กต์ ViewModel ทํางานโดยอัตโนมัติ

วิธีเชื่อมโยง ViewModel กับเลย์เอาต์

  • เพิ่มตัวแปรการเชื่อมโยงของข้อมูลประเภท ViewModel ในไฟล์เลย์เอาต์
   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  • ในไฟล์ GameFragment ให้ส่ง GameViewModel ไปยังการเชื่อมโยงข้อมูล
binding.gameViewModel = viewModel

การเชื่อมโยง Listener

  • การเชื่อมโยง Listener คือนิพจน์การเชื่อมโยงในเลย์เอาต์ที่ทํางานเมื่อมีการเรียกเหตุการณ์การคลิก เช่น onClick()
  • การเชื่อมโยง Listener จะถูกเขียนเป็นนิพจน์แลมบ์ดา
  • เมื่อใช้การเชื่อมโยง Listener คุณสามารถแทนที่ Listener การคลิกในตัวควบคุม UI ด้วย Listener การเชื่อมโยงในไฟล์เลย์เอาต์
  • การเชื่อมโยงข้อมูลจะสร้าง Listener และตั้งค่า Listener ในข้อมูลพร็อพเพอร์ตี้
 android:onClick="@{() -> gameViewModel.onSkip()}"

การเพิ่ม LiveData ไปยังการเชื่อมโยงข้อมูล

  • คุณใช้ออบเจ็กต์ LiveData เป็นแหล่งข้อมูลแบบผูกข้อมูลเพื่อแจ้ง UI โดยอัตโนมัติเกี่ยวกับการเปลี่ยนแปลงข้อมูลได้
  • คุณสามารถเชื่อมโยงมุมมองกับออบเจ็กต์ LiveData ใน ViewModel ได้โดยตรง เมื่อ LiveData ใน ViewModel อัปเดต มุมมองในเลย์เอาต์อาจอัปเดตโดยอัตโนมัติโดยไม่มีเมธอดการสังเกตการณ์ในตัวควบคุม UI
android:text="@{gameViewModel.word}"
  • เพื่อให้การเชื่อมโยงข้อมูล LiveData ทํางานได้ ให้ตั้งค่ากิจกรรมปัจจุบัน (ตัวควบคุม UI) เป็นเจ้าของวงจรของตัวแปร binding ในตัวควบคุม UI
binding.lifecycleOwner = this

การจัดรูปแบบสตริงด้วยการเชื่อมโยงข้อมูล

  • คุณสามารถเชื่อมโยงทรัพยากรสตริงกับตัวยึดตําแหน่ง เช่น %s สําหรับสตริงและ %d สําหรับจํานวนเต็มโดยใช้การเชื่อมโยงข้อมูล
  • หากต้องการอัปเดตแอตทริบิวต์ text ของข้อมูลพร็อพเพอร์ตี้ ให้ส่งผ่านออบเจ็กต์ LiveData เป็นอาร์กิวเมนต์ไปยังสตริงการจัดรูปแบบ
 android:text="@{@string/quote_format(gameViewModel.word)}"

หลักสูตร Udacity:

เอกสารประกอบสําหรับนักพัฒนาซอฟต์แวร์ Android

ส่วนนี้จะอธิบายการบ้านและรายงานสําหรับนักเรียนที่ทํางานผ่าน Codelab นี้ซึ่งเป็นส่วนหนึ่งของหลักสูตรที่นําโดยผู้สอน สิ่งที่ผู้สอนต้องทํามีดังนี้

  • มอบหมายการบ้านหากจําเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานทําการบ้าน
  • ตัดเกรดการบ้าน

ผู้สอนจะใช้คําแนะนําเหล่านี้เท่าใดก็ได้หรือตามที่ต้องการก็ได้ และสามารถกําหนดให้การบ้านอื่นๆ ที่ตนคิดว่าเหมาะสมได้

หากคุณใช้ Codelab ด้วยตัวเอง ก็ให้ใช้การบ้านเพื่อทดสอบความรู้ของคุณได้

ตอบคําถามเหล่านี้

คำถามที่ 1

ข้อความใดต่อไปนี้ที่ไม่เป็นจริงเกี่ยวกับการเชื่อมโยงผู้ฟัง

  • Listener การเชื่อมโยงคือนิพจน์การเชื่อมโยงที่จะทํางานเมื่อมีเหตุการณ์เกิดขึ้น
  • การเชื่อมโยง Listener จะทํางานร่วมกับปลั๊กอิน Android Gradle ทุกเวอร์ชัน
  • การเชื่อมโยง Listener จะถูกเขียนเป็นนิพจน์แลมบ์ดา
  • การเชื่อมโยง Listener คล้ายกับการอ้างอิงเมธอด แต่ช่วยให้คุณสามารถเรียกใช้นิพจน์การเชื่อมโยงข้อมูลที่กําหนดเองได้

คำถามที่ 2

สมมติว่าแอปของคุณมีทรัพยากรสตริงนี้:
<string name="generic_name">Hello %s</string>

ข้อใดต่อไปนี้เป็นไวยากรณ์ที่ถูกต้องสําหรับการจัดรูปแบบสตริงโดยใช้นิพจน์การเชื่อมโยงข้อมูล

  • 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}"

คำถามที่ 3

นิพจน์การเชื่อมโยงผู้ฟังจะได้รับการประเมินและเรียกใช้เมื่อใด

  • ข้อมูลที่เปลี่ยนแปลงของ LiveData มีการเปลี่ยนแปลง
  • เมื่อมีการสร้างกิจกรรมอีกครั้งโดยการเปลี่ยนแปลงการกําหนดค่า
  • เมื่อมีเหตุการณ์อย่างเช่น onClick() เกิดขึ้น
  • เวลาที่กิจกรรมเข้าสู่พื้นหลัง

เริ่มบทเรียนถัดไป: 5.4: การเปลี่ยนรูปแบบข้อมูลแบบสด

สําหรับลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ โปรดดูหน้า Landing Page ของ Codelab ของ Android Kotlin Fundamentals