หลักพื้นฐานของ Android Kotlin 07.4: การโต้ตอบกับรายการ RecyclerView

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

บทนำ

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

ใน Codelab นี้ คุณจะเพิ่มการโต้ตอบลงใน RecyclerView โดยต่อยอดจากแอปติดตามการนอนหลับเวอร์ชันขยายจาก Codelab ชุดก่อนหน้า

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

  • การสร้างอินเทอร์เฟซผู้ใช้พื้นฐานโดยใช้กิจกรรม Fragment และมุมมอง
  • การไปยังส่วนย่อยต่างๆ และการใช้ safeArgs เพื่อส่งข้อมูลระหว่างส่วนย่อย
  • ดูโมเดล โรงงานโมเดล การแปลง และ LiveData รวมถึง Observer ของโมเดล
  • วิธีสร้างRoomฐานข้อมูล สร้างออบเจ็กต์การเข้าถึงข้อมูล (DAO) และกำหนดเอนทิตี
  • วิธีใช้โครูทีนสำหรับฐานข้อมูลและงานอื่นๆ ที่ใช้เวลานาน
  • วิธีใช้ RecyclerView พื้นฐานกับ Adapter, ViewHolder และเลย์เอาต์รายการ
  • วิธีใช้การเชื่อมโยงข้อมูลสำหรับ RecyclerView
  • วิธีสร้างและใช้อะแดปเตอร์การเชื่อมโยงเพื่อเปลี่ยนรูปแบบข้อมูล
  • วิธีใช้ GridLayoutManager

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

  • วิธีทำให้รายการใน RecyclerView คลิกได้ ใช้เครื่องมือฟังการคลิกเพื่อไปยังมุมมองรายละเอียดเมื่อมีการคลิกรายการ

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

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

แอปติดตามการนอนหลับเริ่มต้นมี 2 หน้าจอซึ่งแสดงด้วย Fragment ดังที่แสดงในรูปภาพด้านล่าง

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

แอปนี้ใช้สถาปัตยกรรมที่เรียบง่ายพร้อมตัวควบคุม UI, โมเดลมุมมอง และ LiveData รวมถึงฐานข้อมูล Room เพื่อจัดเก็บข้อมูลการนอนหลับ

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

ขั้นตอนที่ 1: ดาวน์โหลดแอปเริ่มต้น

  1. ดาวน์โหลดโค้ด RecyclerViewClickHandler-Starter จาก GitHub แล้วเปิดโปรเจ็กต์ใน Android Studio
  2. สร้างและเรียกใช้แอปติดตามการนอนหลับเริ่มต้น

[ไม่บังคับ] อัปเดตแอปหากต้องการใช้แอปจากโค้ดแล็บก่อนหน้า

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

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

  1. แม้ว่าคุณจะใช้แอปที่มีอยู่ต่อไป แต่ให้รับโค้ด RecyclerViewClickHandler-Starter จาก GitHub เพื่อให้คุณคัดลอกไฟล์ได้
  2. คัดลอกไฟล์ทั้งหมดในแพ็กเกจ sleepdetail
  3. ในlayoutโฟลเดอร์ ให้คัดลอกไฟล์ fragment_sleep_detail.xml
  4. คัดลอกเนื้อหาที่อัปเดตของ navigation.xml ซึ่งจะเพิ่มการนำทางสำหรับ sleep_detail_fragment
  5. ในdatabaseแพ็กเกจSleepDatabaseDao ให้เพิ่มเมธอด getNightWithId() ใหม่
/**
 * Selects and returns the night with given nightId.
*/
@Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
fun getNightWithId(key: Long): LiveData<SleepNight>
  1. ใน res/values/strings ให้เพิ่มทรัพยากรสตริงต่อไปนี้
<string name="close">Close</string>
  1. ล้างและสร้างแอปใหม่เพื่ออัปเดตการเชื่อมโยงข้อมูล

ขั้นตอนที่ 2: ตรวจสอบโค้ดสำหรับหน้าจอรายละเอียดการนอนหลับ

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

  1. ค้นหาแพ็กเกจ sleepdetail ในแอป แพ็กเกจนี้มี Fragment, ViewModel และ ViewModelFactory สำหรับ Fragment ที่แสดงรายละเอียดการนอนหลับ 1 คืน

  2. ในแพ็กเกจ sleepdetail ให้เปิดและตรวจสอบโค้ดสำหรับ SleepDetailViewModel โมเดลมุมมองนี้ใช้คีย์สำหรับ SleepNight และ DAO ในตัวสร้าง

    เนื้อหาของคลาสมีโค้ดเพื่อรับ SleepNight สำหรับคีย์ที่ระบุ และตัวแปร navigateToSleepTracker เพื่อควบคุมการนำทางกลับไปยัง SleepTrackerFragment เมื่อกดปุ่มปิด

    ฟังก์ชัน getNightWithId() จะแสดงผล LiveData<SleepNight> และกำหนดไว้ใน SleepDatabaseDao (ในแพ็กเกจ database)

  3. ในแพ็กเกจ sleepdetail ให้เปิดและตรวจสอบโค้ดสำหรับ SleepDetailFragment โปรดสังเกตการตั้งค่าการเชื่อมโยงข้อมูล, View Model และ Observer สำหรับการนำทาง

  4. ในแพ็กเกจ sleepdetail ให้เปิดและตรวจสอบโค้ดสำหรับ SleepDetailViewModelFactory

  5. ตรวจสอบ fragment_sleep_detail.xml ในโฟลเดอร์เลย์เอาต์ โปรดสังเกตตัวแปร sleepDetailViewModel ที่กำหนดไว้ในแท็ก <data> เพื่อรับข้อมูลที่จะแสดงในแต่ละมุมมองจาก View Model

    เลย์เอาต์มี ConstraintLayout ที่มี ImageView สำหรับคุณภาพการนอนหลับ TextView สำหรับการให้คะแนนคุณภาพ TextView สำหรับระยะเวลาการนอนหลับ และ Button เพื่อปิดรายละเอียด Fragment

  6. เปิดไฟล์ navigation.xml สำหรับ sleep_tracker_fragment ให้สังเกตการดำเนินการใหม่สำหรับ sleep_detail_fragment

    การดำเนินการใหม่ action_sleep_tracker_fragment_to_sleepDetailFragment คือการไปยังส่วนต่างๆ จาก Fragment เครื่องมือติดตามการนอนหลับไปยังหน้าจอรายละเอียด

ในงานนี้ คุณจะอัปเดต RecyclerView เพื่อตอบสนองต่อการแตะของผู้ใช้โดยแสดงหน้าจอรายละเอียดสำหรับรายการที่แตะ

การรับและการจัดการคลิกเป็นงาน 2 ส่วน โดยก่อนอื่นคุณต้องรับฟังและรับคลิก รวมถึงพิจารณาว่ามีการคลิกรายการใด จากนั้นคุณต้องตอบสนองต่อการคลิกด้วยการดำเนินการ

แล้วที่ไหนเหมาะที่สุดที่จะเพิ่มเครื่องมือฟังการคลิกสำหรับแอปนี้

  • SleepTrackerFragment มีมุมมองมากมาย ดังนั้นการฟังเหตุการณ์คลิกที่ระดับ Fragment จะไม่บอกว่ามีการคลิกรายการใด และจะไม่บอกด้วยว่าเป็นการคลิกรายการหรือองค์ประกอบ UI อื่นๆ
  • การฟังที่ระดับ RecyclerView ทำให้ยากที่จะระบุได้อย่างแม่นยำว่าผู้ใช้คลิกรายการใดในรายการ
  • อัตราที่ดีที่สุดในการรับข้อมูลเกี่ยวกับรายการที่คลิกคือในออบเจ็กต์ ViewHolder เนื่องจากแสดงถึงรายการในลิสต์ 1 รายการ

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

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

ขั้นตอนที่ 1: สร้างเครื่องมือฟังการคลิกและเรียกใช้จากเลย์เอาต์ของรายการ

  1. ในโฟลเดอร์ sleeptracker ให้เปิด SleepNightAdapter.kt
  2. ที่ส่วนท้ายของไฟล์ ในระดับบนสุด ให้สร้างคลาส Listener ใหม่ SleepNightListener
class SleepNightListener() {
    
}
  1. เพิ่มonClick()ฟังก์ชันภายในSleepNightListenerคลาส เมื่อมีการคลิกมุมมองที่แสดงรายการ ระบบจะเรียกใช้onClick()ฟังก์ชันนี้ (คุณจะตั้งค่าพร็อพเพอร์ตี้ android:onClick ของข้อมูลพร็อพเพอร์ตี้ในภายหลังเป็นฟังก์ชันนี้)
class SleepNightListener() {
    fun onClick() = 
}
  1. เพิ่มอาร์กิวเมนต์ฟังก์ชัน night ของประเภท SleepNight ไปยัง onClick() มุมมองทราบว่ากำลังแสดงรายการใด และต้องส่งต่อข้อมูลดังกล่าวเพื่อจัดการการคลิก
class SleepNightListener() {
    fun onClick(night: SleepNight) = 
}
  1. หากต้องการกำหนดว่า onClick() ทำอะไร ให้ระบุการเรียกกลับ clickListener ในตัวสร้างของ SleepNightListener แล้วกำหนดให้ onClick()

    การตั้งชื่อ Lambda ที่จัดการการคลิก clickListener จะช่วยให้ติดตามได้เมื่อมีการส่งผ่านระหว่างคลาส การเรียกกลับ clickListener ต้องการเพียง night.nightId เพื่อเข้าถึงข้อมูลจากฐานข้อมูล SleepNightListener คลาสที่เสร็จสมบูรณ์แล้วควรมีลักษณะเหมือนโค้ดด้านล่าง
class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
   fun onClick(night: SleepNight) = clickListener(night.nightId)
}
  1. เปิด list_item_sleep_night.xml.
  2. ภายในบล็อก data ให้เพิ่มตัวแปรใหม่เพื่อให้คลาส SleepNightListener พร้อมใช้งานผ่านการเชื่อมโยงข้อมูล ตั้ง<variable>ใหม่เป็นnameของclickListener. ตั้งค่าtypeเป็นชื่อที่สมบูรณ์ของคลาส com.example.android.trackmysleepquality.sleeptracker.SleepNightListener ดังที่แสดงด้านล่าง ตอนนี้คุณเข้าถึงฟังก์ชัน onClick() ใน SleepNightListener จากเลย์เอาต์นี้ได้แล้ว
<variable
            name="clickListener"
            type="com.example.android.trackmysleepquality.sleeptracker.SleepNightListener" />
  1. หากต้องการฟังการคลิกส่วนใดส่วนหนึ่งของรายการนี้ ให้เพิ่มแอตทริบิวต์ android:onClick ลงใน ConstraintLayout

    ตั้งค่าแอตทริบิวต์เป็น clickListener:onClick(sleep) โดยใช้ Lambda การเชื่อมโยงข้อมูล ดังที่แสดงด้านล่าง
android:onClick="@{() -> clickListener.onClick(sleep)}"

ขั้นตอนที่ 2: ส่งเครื่องมือฟังการคลิกไปยังตัวยึดมุมมองและออบเจ็กต์การเชื่อมโยง

  1. เปิด SleepNightAdapter.kt
  2. แก้ไขตัวสร้างของคลาส SleepNightAdapter เพื่อรับ val clickListener: SleepNightListener เมื่ออะแดปเตอร์เชื่อมโยง ViewHolder จะต้องระบุเครื่องมือฟังการคลิกนี้
class SleepNightAdapter(val clickListener: SleepNightListener):
       ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
  1. ใน onBindViewHolder() ให้อัปเดตการเรียกไปยัง holder.bind() เพื่อส่งต่อ Listener การคลิกไปยัง ViewHolder ด้วย คุณจะได้รับข้อผิดพลาดของคอมไพเลอร์เนื่องจากคุณเพิ่มพารามิเตอร์ลงในการเรียกฟังก์ชัน
holder.bind(getItem(position)!!, clickListener)
  1. เพิ่มพารามิเตอร์ clickListener ลงใน bind() โดยวางเคอร์เซอร์บนข้อผิดพลาด แล้วกด Alt+Enter (Windows) หรือ Option+Enter (Mac) บนข้อผิดพลาดเพื่อดูรายละเอียด ดังที่แสดงในภาพหน้าจอด้านล่าง

  1. ในคลาส ViewHolder ภายในฟังก์ชัน bind() ให้กำหนด Listener การคลิกให้กับออบเจ็กต์ binding คุณเห็นข้อผิดพลาดเนื่องจากต้องอัปเดตออบเจ็กต์การเชื่อมโยง
binding.clickListener = clickListener
  1. หากต้องการอัปเดตการเชื่อมโยงข้อมูล ให้ล้างและสร้างใหม่โปรเจ็กต์ (คุณอาจต้องล้างแคชด้วย) ดังนั้น คุณจึงนำเครื่องมือฟังการคลิกจากตัวสร้างอแดปเตอร์ และส่งไปยังตัวยึดมุมมองและไปยังออบเจ็กต์การเชื่อมโยง

ขั้นตอนที่ 3: แสดงข้อความป๊อปอัปเมื่อแตะรายการ

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

  1. เปิด SleepTrackerFragment.kt
  2. ใน onCreateView() ให้ค้นหาตัวแปร adapter โปรดสังเกตว่าระบบแสดงข้อผิดพลาดเนื่องจากตอนนี้คาดหวังพารามิเตอร์ Listener การคลิก
  3. กำหนดเครื่องมือฟังการคลิกโดยส่งผ่าน Lambda ไปยัง SleepNightAdapter Lambda อย่างง่ายนี้จะแสดงข้อความโทสต์ที่แสดง nightId ดังที่แสดงด้านล่าง คุณจะต้องนำเข้า Toast คำจำกัดความที่อัปเดตแล้วฉบับสมบูรณ์มีดังนี้
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
   Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
})
  1. เรียกใช้แอป แตะรายการ และตรวจสอบว่ารายการแสดงข้อความป๊อปอัปพร้อม nightId ที่ถูกต้อง เนื่องจากรายการมีค่า nightId เพิ่มขึ้น และแอปจะแสดงคืนล่าสุดก่อน รายการที่มีค่า nightId ต่ำสุดจึงอยู่ด้านล่างของรายการ

ในงานนี้ คุณจะเปลี่ยนลักษณะการทำงานเมื่อมีการคลิกรายการใน RecyclerView เพื่อให้แอปไปยังรายละเอียด Fragment ที่แสดงข้อมูลเพิ่มเติมเกี่ยวกับคืนที่คลิกแทนที่จะแสดงข้อความ Toast

ขั้นตอนที่ 1: ไปที่หน้าเว็บเมื่อคลิก

ในขั้นตอนนี้ แทนที่จะแสดงเพียงข้อความป๊อปอัป คุณจะเปลี่ยน Lambda ของเครื่องมือฟังการคลิกใน onCreateView() ของ SleepTrackerFragment เพื่อส่ง nightId ไปยัง SleepTrackerViewModel และทริกเกอร์การนำทางไปยัง SleepDetailFragment

กำหนดฟังก์ชันตัวแฮนเดิลการคลิก

  1. เปิด SleepTrackerViewModel.kt
  2. ภายใน SleepTrackerViewModel ให้กําหนดonSleepNightClicked()ฟังก์ชันตัวแฮนเดิลการคลิกที่ส่วนท้าย
fun onSleepNightClicked(id: Long) {

}
  1. ภายใน onSleepNightClicked() ให้ทริกเกอร์การนำทางโดยตั้งค่า _navigateToSleepDetail เป็น id ที่ส่งมาของคืนที่คลิก
fun onSleepNightClicked(id: Long) {
   _navigateToSleepDetail.value = id
}
  1. นำ _navigateToSleepDetail มาใช้ กำหนด private MutableLiveData สำหรับสถานะการนำทางเช่นเดียวกับที่เคยทำ และมี val ที่รับได้แบบสาธารณะ
private val _navigateToSleepDetail = MutableLiveData<Long>()
val navigateToSleepDetail
   get() = _navigateToSleepDetail
  1. กำหนดวิธีการเรียกใช้หลังจากที่แอปนำทางเสร็จแล้ว ตั้งชื่อว่า onSleepDetailNavigated() และตั้งค่าเป็น null
fun onSleepDetailNavigated() {
    _navigateToSleepDetail.value = null
}

เพิ่มโค้ดเพื่อเรียกตัวแฮนเดิลการคลิก

  1. เปิด SleepTrackerFragment.kt แล้วเลื่อนลงไปที่โค้ดที่สร้างอแดปเตอร์และกำหนด SleepNightListener เพื่อแสดงข้อความ Toast
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
   Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
})
  1. เพิ่มโค้ดต่อไปนี้ใต้ข้อความป๊อปอัปเพื่อเรียกตัวแฮนเดิลการคลิก onSleepNighClicked() ใน sleepTrackerViewModel เมื่อมีการแตะรายการ ส่งใน nightId เพื่อให้ ViewModel รู้ว่าควรรับคืนการนอนหลับคืนใด ซึ่งจะทำให้เกิดข้อผิดพลาดเนื่องจากคุณยังไม่ได้กำหนด onSleepNightClicked() คุณจะเก็บ แสดงความคิดเห็น หรือลบข้อความแจ้งก็ได้ตามต้องการ
sleepTrackerViewModel.onSleepNightClicked(nightId)

เพิ่มโค้ดเพื่อสังเกตการคลิก

  1. เปิด SleepTrackerFragment.kt
  2. ใน onCreateView() เหนือประกาศของ manager ให้เพิ่มโค้ดเพื่อสังเกต navigateToSleepDetail LiveData ใหม่ เมื่อ navigateToSleepDetail เปลี่ยนแปลง ให้ไปที่ SleepDetailFragment โดยส่ง night แล้วเรียกใช้ onSleepDetailNavigated() หลังจากนั้น เนื่องจากคุณเคยทำสิ่งนี้ในโค้ดแล็บก่อนหน้านี้แล้ว เราจึงขอเสนอโค้ดดังนี้
sleepTrackerViewModel.navigateToSleepDetail.observe(this, Observer { night ->
            night?.let {
              this.findNavController().navigate(
                        SleepTrackerFragmentDirections
                                .actionSleepTrackerFragmentToSleepDetailFragment(night))
               sleepTrackerViewModel.onSleepDetailNavigated()
            }
        })
  1. เรียกใช้โค้ด คลิกที่รายการ แล้วแอปก็ขัดข้อง

จัดการค่า Null ใน Binding Adapter ดังนี้

  1. เรียกใช้แอปอีกครั้งในโหมดแก้ไขข้อบกพร่อง แตะรายการ แล้วกรองบันทึกเพื่อแสดงข้อผิดพลาด โดยจะแสดง Stack Trace ซึ่งรวมถึงข้อมูลที่คล้ายกับข้อมูลด้านล่าง
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter item

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

อย่างไรก็ตาม กลไกการจัดการคลิกแบบใหม่นี้ทำให้ตอนนี้ Binding Adapter สามารถเรียกใช้ด้วยค่า null สำหรับ item ได้แล้ว โดยเฉพาะอย่างยิ่ง เมื่อแอปเริ่มต้น LiveData จะเริ่มต้นเป็น null คุณจึงต้องเพิ่มการตรวจสอบค่า Null ลงในแต่ละอแดปเตอร์

  1. ใน BindingUtils.kt ให้เปลี่ยนประเภทอาร์กิวเมนต์ item เป็น Nullable สำหรับ Binding Adapter แต่ละรายการ แล้วห่อเนื้อหาด้วย item?.let{...} เช่น อะแดปเตอร์สำหรับ sleepQualityString จะมีลักษณะดังนี้ เปลี่ยนอแดปเตอร์อื่นๆ ในลักษณะเดียวกัน
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight?) {
   item?.let {
       text = convertNumericQualityToString(item.sleepQuality, context.resources)
   }
}
  1. เรียกใช้แอป แตะรายการ แล้วมุมมองรายละเอียดจะเปิดขึ้น

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

หากต้องการให้รายการใน RecyclerView ตอบสนองต่อการคลิก ให้แนบเครื่องมือฟังการคลิกกับรายการใน ViewHolder และจัดการการคลิกใน ViewModel

หากต้องการให้รายการใน RecyclerView ตอบสนองต่อการคลิก คุณต้องทำดังนี้

  • สร้างคลาส Listener ที่ใช้ Lambda และกำหนดให้กับฟังก์ชัน onClick()
class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
   fun onClick(night: SleepNight) = clickListener(night.nightId)
}
  • ตั้งค่าเครื่องมือฟังการคลิกในมุมมอง
android:onClick="@{() -> clickListener.onClick(sleep)}"
  • ส่งเครื่องมือฟังการคลิกไปยังตัวสร้างอะแดปเตอร์ ไปยังตัวยึดมุมมอง และเพิ่มลงในออบเจ็กต์การเชื่อมโยง
class SleepNightAdapter(val clickListener: SleepNightListener):
       ListAdapter<DataItem, RecyclerView.ViewHolder>(SleepNightDiffCallback()
holder.bind(getItem(position)!!, clickListener)
binding.clickListener = clickListener
  • ใน Fragment ที่แสดง RecyclerView ซึ่งคุณสร้าง Adapter ให้กำหนดเครื่องมือฟังการคลิกโดยส่ง Lambda ไปยัง Adapter
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
      sleepTrackerViewModel.onSleepNightClicked(nightId)
})
  • ใช้ตัวแฮนเดิลการคลิกใน ViewModel สําหรับการคลิกรายการในลิสต์ โดยทั่วไปแล้วการคลิกนี้จะทําให้เกิดการไปยังส่วนย่อยรายละเอียด

หลักสูตร Udacity:

เอกสารประกอบสำหรับนักพัฒนาแอป Android

ส่วนนี้แสดงรายการการบ้านที่เป็นไปได้สำหรับนักเรียน/นักศึกษาที่กำลังทำ Codelab นี้เป็นส่วนหนึ่งของหลักสูตรที่สอนโดยผู้สอน ผู้สอนมีหน้าที่ดำเนินการต่อไปนี้

  • มอบหมายการบ้านหากจำเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานที่ได้รับมอบหมาย
  • ให้คะแนนงานการบ้าน

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

หากคุณกำลังทำ Codelab นี้ด้วยตนเอง โปรดใช้แบบฝึกหัดเหล่านี้เพื่อทดสอบความรู้ของคุณ

ตอบคำถามต่อไปนี้

คำถามที่ 1

สมมติว่าแอปของคุณมี RecyclerView ที่แสดงรายการในช็อปปิ้งลิสต์ แอปของคุณยังกำหนดคลาสตัวฟังการคลิกด้วย

class ShoppingListItemListener(val clickListener: (itemId: Long) -> Unit) {
    fun onClick(cartItem: CartItem) = clickListener(cartItem.itemId)
}

คุณจะทำให้ ShoppingListItemListener พร้อมใช้งานกับการเชื่อมโยงข้อมูลได้อย่างไร เลือกหนึ่งรายการ

▢ ในไฟล์เลย์เอาต์ที่มี RecyclerView ที่แสดงรายการช็อปปิ้ง ให้เพิ่มตัวแปร <data> สำหรับ ShoppingListItemListener

▢ ในไฟล์เลย์เอาต์ที่กำหนดเลย์เอาต์สำหรับแถวเดียวในรายการช็อปปิ้ง ให้เพิ่มตัวแปร <data> สำหรับ ShoppingListItemListener

▢ ในคลาส ShoppingListItemListener ให้เพิ่มฟังก์ชันเพื่อเปิดใช้การเชื่อมโยงข้อมูล

fun onBinding (cartItem: CartItem) {dataBindingEnable(true)}

▢ ในคลาส ShoppingListItemListener ภายในฟังก์ชัน onClick() ให้เพิ่มการเรียกเพื่อเปิดใช้การเชื่อมโยงข้อมูล

fun onClick(cartItem: CartItem) = { 
    clickListener(cartItem.itemId)
    dataBindingEnable(true)
}

คำถามที่ 2

คุณจะเพิ่มแอตทริบิวต์ android:onClick ที่ทำให้สินค้าใน RecyclerView ตอบสนองต่อการคลิกได้ที่ใด เลือกได้มากกว่า 1 ข้อ

▢ ในไฟล์เลย์เอาต์ที่แสดง RecyclerView ให้เพิ่มลงใน <androidx.recyclerview.widget.RecyclerView>

▢ เพิ่มลงในไฟล์เลย์เอาต์ของรายการในแถว หากต้องการให้ทั้งรายการคลิกได้ ให้เพิ่มรายการลงในมุมมองระดับบนที่มีรายการในแถว

▢ เพิ่มลงในไฟล์เลย์เอาต์ของรายการในแถว หากต้องการให้ TextView รายการเดียวในรายการคลิกได้ ให้เพิ่มลงใน <TextView>

▢ เพิ่มลงในไฟล์เลย์เอาต์สำหรับ MainActivity เสมอ

เริ่มบทเรียนถัดไป: 7.5: ส่วนหัวใน RecyclerView