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 ก่อนหน้าเป็นโค้ดเริ่มต้น หรือจะดาวน์โหลดแอปเริ่มต้นก็ได้
- (ไม่บังคับ) หากไม่ได้ใช้โค้ดจาก Codelab ก่อนหน้า ให้ดาวน์โหลดโค้ดเริ่มต้นสำหรับ Codelab นี้ คลายซิปรหัสและเปิดโปรเจ็กต์ใน Android Studio
- เรียกใช้แอปและเล่นเกม
- โปรดทราบว่าปุ่มรับทราบจะแสดงคำถัดไปและเพิ่มคะแนนขึ้น 1 คะแนน ส่วนปุ่มข้ามจะแสดงคำถัดไปและลดคะแนนลง 1 คะแนน ปุ่มจบเกมจะสิ้นสุดเกม
- เลื่อนดูคำทั้งหมด แล้วสังเกตว่าแอปจะไปยังหน้าจอคะแนนโดยอัตโนมัติ
ในโค้ดแล็บก่อนหน้านี้ คุณใช้การเชื่อมโยงข้อมูลเป็นวิธีที่ปลอดภัยต่อประเภทในการเข้าถึงมุมมองในแอป 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
- ใน
game_fragment.xml
ให้เพิ่มตัวแปรการเชื่อมโยงข้อมูลประเภทGameViewModel
หากมีข้อผิดพลาดใน Android Studio ให้ล้างและสร้างโปรเจ็กต์ใหม่
<layout ...>
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
<androidx.constraintlayout...
- ในไฟล์
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
- ใน
game_fragment.xml
ให้เพิ่มแอตทริบิวต์onClick
ลงในskip_button
กำหนดนิพจน์การเชื่อมโยงและเรียกใช้เมธอดonSkip()
ในGameViewModel
นิพจน์การเชื่อมโยงนี้เรียกว่าการเชื่อมโยง Listener
<Button
android:id="@+id/skip_button"
...
android:onClick="@{() -> gameViewModel.onSkip()}"
... />
- ในทำนองเดียวกัน ให้เชื่อมโยงเหตุการณ์คลิกของ
correct_button
กับเมธอดonCorrect
()
ในGameViewModel
<Button
android:id="@+id/correct_button"
...
android:onClick="@{() -> gameViewModel.onCorrect()}"
... />
- เชื่อมโยงเหตุการณ์คลิกของ
end_game_button
กับเมธอดonGameFinish
()
ในGameViewModel
<Button
android:id="@+id/end_game_button"
...
android:onClick="@{() -> gameViewModel.onGameFinish()}"
... />
- ใน
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
- ในไฟล์
score_fragment.xml
ให้เพิ่มตัวแปรการเชื่อมโยงประเภทScoreViewModel
ขั้นตอนนี้คล้ายกับที่คุณทำสำหรับGameViewModel
ด้านบน
<layout ...>
<data>
<variable
name="scoreViewModel"
type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
- ใน
score_fragment.xml
ให้เพิ่มแอตทริบิวต์onClick
ลงในplay_again_button
กำหนดการเชื่อมโยง Listener และเรียกใช้เมธอดonPlayAgain()
ในScoreViewModel
<Button
android:id="@+id/play_again_button"
...
android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
... />
- ใน
ScoreFragment
ภายในonCreateView()
ให้เริ่มต้นviewModel
จากนั้นเริ่มต้นตัวแปรการเชื่อมโยงbinding.scoreViewModel
viewModel = ...
binding.scoreViewModel = viewModel
- ใน
ScoreFragment
ให้นำโค้ดที่ตั้งค่าเครื่องมือฟังการคลิกสำหรับplayAgainButton
ออก หาก Android Studio แสดงข้อผิดพลาด ให้ล้างและสร้างโปรเจ็กต์ใหม่
โค้ดที่จะนำออก
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- เรียกใช้แอป แอปควรทำงานได้เหมือนเดิม แต่ตอนนี้มุมมองปุ่มจะสื่อสารกับออบเจ็กต์
ViewModel
โดยตรง มุมมองจะไม่สื่อสารผ่านตัวแฮนเดิลการคลิกปุ่มในScoreFragment
อีกต่อไป
การแก้ปัญหาข้อความแสดงข้อผิดพลาดในการเชื่อมโยงข้อมูล
เมื่อแอปใช้การเชื่อมโยงข้อมูล กระบวนการคอมไพล์จะสร้างคลาสกลางที่ใช้สำหรับการเชื่อมโยงข้อมูล แอปอาจมีข้อผิดพลาดที่ Android Studio ตรวจไม่พบจนกว่าคุณจะพยายามคอมไพล์แอป ดังนั้นคุณจึงไม่เห็นคำเตือนหรือโค้ดสีแดงขณะเขียนโค้ด แต่ในเวลาคอมไพล์ คุณจะได้รับข้อผิดพลาดที่เข้าใจยากซึ่งมาจากคลาสกลางที่สร้างขึ้น
หากได้รับข้อความแสดงข้อผิดพลาดที่อ่านไม่ออก ให้ทำดังนี้
- ดูข้อความในบานหน้าต่างสร้างของ Android Studio อย่างละเอียด หากเห็นตำแหน่งที่ลงท้ายด้วย
databinding
แสดงว่ามีข้อผิดพลาดเกี่ยวกับการเชื่อมโยงข้อมูล - ในไฟล์ XML ของเลย์เอาต์ ให้ตรวจสอบข้อผิดพลาดในแอตทริบิวต์
onClick
ที่ใช้การเชื่อมโยงข้อมูล มองหาฟังก์ชันที่นิพจน์ Lambda เรียกใช้ และตรวจสอบว่าฟังก์ชันนั้นมีอยู่ - ในส่วน
<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
โดยตรง
- ใน
game_fragment.xml
ให้เพิ่มแอตทริบิวต์android:text
ลงในมุมมองข้อความword_text
ตั้งค่าเป็นออบเจ็กต์ LiveData
จาก GameViewModel
โดยใช้ตัวแปรการเชื่อมโยง gameViewModel
word
<TextView
android:id="@+id/word_text"
...
android:text="@{gameViewModel.word}"
... />
โปรดทราบว่าคุณไม่จำเป็นต้องใช้ word.value
แต่คุณสามารถใช้ออบเจ็กต์ LiveData
จริงแทนได้ ออบเจ็กต์ LiveData
จะแสดงค่าปัจจุบันของ word
หากค่าของ word
เป็น Null ออบเจ็กต์ LiveData
จะแสดงสตริงว่าง
- ใน
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
- ใน
GameFragment
ให้นำผู้สังเกตการณ์สำหรับLiveData
word
ออก
โค้ดที่จะนำออก
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
- เรียกใช้แอปและเล่นเกม ตอนนี้ระบบจะอัปเดตคำปัจจุบันโดยไม่มีเมธอด Observer ในตัวควบคุม UI
ขั้นตอนที่ 2: เพิ่ม LiveData ของคะแนนลงในไฟล์ score_fragment.xml
ในขั้นตอนนี้ คุณจะเชื่อมโยง LiveData
score
กับมุมมองข้อความคะแนนในส่วนคะแนน
- ใน
score_fragment.xml
ให้เพิ่มแอตทริบิวต์android:text
ลงในมุมมองข้อความคะแนน กำหนดscoreViewModel.score
ให้กับแอตทริบิวต์text
เนื่องจากscore
เป็นจำนวนเต็ม ให้แปลงเป็นสตริงโดยใช้String.valueOf()
<TextView
android:id="@+id/score_text"
...
android:text="@{String.valueOf(scoreViewModel.score)}"
... />
- ใน
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
- ใน
ScoreFragment
ให้นำ Observer สำหรับออบเจ็กต์score
ออก
โค้ดที่จะนำออก
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- เรียกใช้แอปและเล่นเกม โปรดสังเกตว่าคะแนนในส่วนคะแนนจะแสดงอย่างถูกต้องโดยไม่มี Observer ในส่วนคะแนน
ขั้นตอนที่ 3: เพิ่มการจัดรูปแบบสตริงด้วยการเชื่อมโยงข้อมูล
ในเลย์เอาต์ คุณสามารถเพิ่มการจัดรูปแบบสตริงพร้อมกับการเชื่อมโยงข้อมูลได้ ในงานนี้ คุณจะจัดรูปแบบคำปัจจุบันเพื่อเพิ่มเครื่องหมายคำพูดรอบคำ นอกจากนี้ คุณยังจัดรูปแบบสตริงคะแนนให้มีคำนำหน้าเป็น คะแนนปัจจุบัน ได้ด้วย ดังที่แสดงในรูปภาพต่อไปนี้
- ใน
string.xml
ให้เพิ่มสตริงต่อไปนี้ ซึ่งคุณจะใช้เพื่อจัดรูปแบบมุมมองข้อความword
และscore
%s
และ%d
เป็นตัวยึดตำแหน่งสำหรับคำปัจจุบันและคะแนนปัจจุบัน
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
- ใน
game_fragment.xml
ให้อัปเดตแอตทริบิวต์text
ของมุมมองข้อความword_text
เพื่อใช้ทรัพยากรสตริงquote_format
ผ่านในgameViewModel.word
ซึ่งจะส่งคำปัจจุบันเป็นอาร์กิวเมนต์ไปยังสตริงการจัดรูปแบบ
<TextView
android:id="@+id/word_text"
...
android:text="@{@string/quote_format(gameViewModel.word)}"
... />
- จัดรูปแบบมุมมองข้อความ
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)}"
... />
- ในคลาส
GameFragment
ภายในเมธอดonCreateView()
ให้นำโค้ด Observerscore
ออก
โค้ดที่จะนำออก
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- ล้างข้อมูล สร้างใหม่ และเรียกใช้แอป แล้วเล่นเกม โปรดสังเกตว่าคำปัจจุบันและคะแนนจะจัดรูปแบบในหน้าจอเกม
ยินดีด้วย คุณได้ผสานรวม 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()
- เมื่อกิจกรรมเข้าสู่เบื้องหลัง
เริ่มบทเรียนถัดไป:
ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin