หลักพื้นฐานของ Android Kotlin 05.3: การเชื่อมโยงข้อมูลกับ ViewModel และ LiveData

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

บทนำ

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

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

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

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

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

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

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

สิ่งที่คุณต้องดำเนินการ

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

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

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

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

ผู้เล่นคนแรกจะแสดงท่าทางตามคำนั้น โดยระมัดระวังไม่ให้พูดคำนั้นออกมาจริงๆ

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

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

หน้าจอชื่อ

หน้าจอเกม

หน้าจอคะแนน

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

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

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

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

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

เช่น

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

Button และ GameViewModel ไม่ได้สื่อสารกันโดยตรง แต่ต้องใช้เครื่องมือฟังการคลิกที่อยู่ใน GameFragment

ViewModel ที่ส่งไปยัง Data Binding

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

ออบเจ็กต์ ViewModel จะเก็บข้อมูล UI ทั้งหมดในแอป GuessTheWord การส่งออบเจ็กต์ ViewModel ไปยัง Data Binding จะช่วยให้คุณทำให้การสื่อสารบางอย่างระหว่าง View กับออบเจ็กต์ 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 จะเขียนเป็นนิพจน์ Lambda

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

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

  1. ใน game_fragment.xml ให้เพิ่มแอตทริบิวต์ onClick ลงใน skip_button กำหนดนิพจน์การเชื่อมโยงและเรียกใช้เมธอด onSkip() ใน GameViewModel นิพจน์การเชื่อมโยงนี้เรียกว่าการเชื่อมโยง Listener
<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 ให้นำคำสั่งที่ตั้งค่าเครื่องมือตรวจหาการคลิกออก และนำฟังก์ชันที่เครื่องมือตรวจหาการคลิกเรียกใช้ออก คุณไม่จำเป็นต้องใช้แล้ว

โค้ดที่จะนำออก

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 ภายใน onCreateView() ให้เริ่มต้น viewModel จากนั้นเริ่มต้นตัวแปรการเชื่อมโยง binding.scoreViewModel
viewModel = ...
binding.scoreViewModel = viewModel
  1. ใน ScoreFragment ให้นำโค้ดที่ตั้งค่าเครื่องมือฟังการคลิกสำหรับ playAgainButton ออก หาก Android Studio แสดงข้อผิดพลาด ให้ล้างและสร้างโปรเจ็กต์ใหม่

โค้ดที่จะนำออก

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

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

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

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

  1. ดูข้อความในบานหน้าต่างสร้างของ Android Studio อย่างละเอียด หากเห็นตำแหน่งที่ลงท้ายด้วย databinding แสดงว่ามีข้อผิดพลาดเกี่ยวกับการเชื่อมโยงข้อมูล
  2. ในไฟล์ XML ของเลย์เอาต์ ให้ตรวจสอบข้อผิดพลาดในแอตทริบิวต์ onClick ที่ใช้การเชื่อมโยงข้อมูล มองหาฟังก์ชันที่นิพจน์ Lambda เรียกใช้ และตรวจสอบว่าฟังก์ชันนั้นมีอยู่
  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 จาก GameViewModel โดยใช้ตัวแปรการเชื่อมโยง gameViewModelword

<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 ให้นำผู้สังเกตการณ์สำหรับ LiveData word ออก

โค้ดที่จะนำออก

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

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

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

  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 ให้นำ Observer สำหรับออบเจ็กต์ score ออก

โค้ดที่จะนำออก

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

ขั้นตอนที่ 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 ภายในเมธอด onCreateView() ให้นำโค้ด Observer score ออก

โค้ดที่จะนำออก

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

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

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

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

การเชื่อมโยงข้อมูล ViewModel

  • คุณเชื่อมโยง 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 จะเขียนเป็นนิพจน์ Lambda
  • การใช้การเชื่อมโยง 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 คือนิพจน์การเชื่อมโยงที่ทํางานเมื่อเกิดเหตุการณ์
  • การเชื่อมโยง Listener ใช้ได้กับปลั๊กอิน Android Gradle ทุกเวอร์ชัน
  • การเชื่อมโยง Listener จะเขียนเป็นนิพจน์ Lambda
  • การเชื่อมโยง 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

ระบบจะประเมินและเรียกใช้นิพจน์ที่เชื่อมโยงกับ Listener เมื่อใด

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

เริ่มบทเรียนถัดไป: 5.4: การแปลง LiveData

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