Android Kotlin Fundamentals 07.2: DiffUtil และการเชื่อมโยงข้อมูลด้วย RecyclerView

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

บทนำ

ใน Codelab ก่อนหน้า คุณได้อัปเดตแอป TrackMySleepคุณภาพ เพื่อแสดงข้อมูลเกี่ยวกับคุณภาพการนอนหลับใน RecyclerView เทคนิคที่ได้เรียนรู้เมื่อสร้าง RecyclerView แรกเพียงพอสําหรับ RecyclerViews ส่วนใหญ่ที่แสดงรายการแบบง่ายที่ไม่ใหญ่เกินไป อย่างไรก็ตาม มีเทคนิคหลายอย่างที่ทําให้ RecyclerView มีประสิทธิภาพมากขึ้นสําหรับรายการขนาดใหญ่และช่วยให้ดูแลรักษาโค้ดและขยายได้ง่ายขึ้นสําหรับรายการและตารางกริดที่ซับซ้อน

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

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

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

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

  • วิธีใช้ DiffUtil เพื่ออัปเดตรายการที่แสดงโดย RecyclerView อย่างมีประสิทธิภาพ
  • วิธีใช้การเชื่อมโยงข้อมูลกับ RecyclerView
  • วิธีใช้อะแดปเตอร์การเชื่อมโยงเพื่อเปลี่ยนรูปแบบข้อมูล

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

  • สร้างจากแอป TrackMySleepคุณภาพ จาก Codelab ก่อนหน้าในชุดนี้
  • อัปเดต SleepNightAdapter เพื่ออัปเดตรายการอย่างมีประสิทธิภาพโดยใช้ DiffUtil
  • ใช้งานการเชื่อมโยงข้อมูลสําหรับ RecyclerView โดยใช้อะแดปเตอร์การเชื่อมโยงในการแปลงข้อมูล

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

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

แอปนี้ออกแบบมาเพื่อใช้ตัวควบคุม UI, ViewModel และ LiveData และฐานข้อมูล Room เพื่อคงข้อมูลการนอนหลับไว้

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

คุณจะใช้แอป SleepTracker ต่อไปจาก Codelab ก่อนหน้าหรือดาวน์โหลดแอป RecyclerViewDiffUtilDataBinding-Starter จาก GitHub ได้

  1. หากจําเป็น ให้ดาวน์โหลดแอป RecyclerViewDiffUtilDataBinding-Starter จาก GitHub และเปิดโปรเจ็กต์ใน Android Studio
  2. เรียกใช้แอป
  3. เปิดไฟล์ SleepNightAdapter.kt
  4. ตรวจสอบโค้ดเพื่อทําความคุ้นเคยกับโครงสร้างของแอป ดูแผนภาพด้านล่างเพื่อดูสรุปการใช้ RecyclerView กับรูปแบบอะแดปเตอร์เพื่อแสดงข้อมูลการนอนหลับให้กับผู้ใช้

  • แอปจะสร้างรายการออบเจ็กต์ SleepNight จากอินพุตของผู้ใช้ วัตถุ SleepNight แต่ละรายการแสดงถึงการนอนหลับ 1 คืน ระยะเวลา และคุณภาพ
  • SleepNightAdapter จะปรับรายการออบเจ็กต์ SleepNight ให้เป็นข้อมูลที่ RecyclerView ใช้และแสดงได้
  • อะแดปเตอร์ SleepNightAdapter จะสร้าง ViewHolders ซึ่งประกอบด้วยข้อมูลพร็อพเพอร์ตี้ ข้อมูล และข้อมูลเมตาสําหรับมุมมองการรีไซเคิลเพื่อแสดงข้อมูล
  • RecyclerView ใช้SleepNightAdapterเพื่อระบุจํานวนรายการที่จะแสดง (getItemCount())RecyclerView ใช้onCreateViewHolder()และonBindViewHolder()เพื่อให้ผู้ถือข้อมูลพร็อพเพอร์ตี้ได้ดูข้อมูล

เมธอดการแจ้งเตือน dataDataSetChanged() ไม่มีประสิทธิภาพ

หากต้องการแจ้ง RecyclerView ว่ารายการมีการเปลี่ยนแปลงและจําเป็นต้องอัปเดต รหัสปัจจุบันจะเรียก notifyDataSetChanged() ใน SleepNightAdapter ดังที่แสดงด้านล่าง

var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

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

หากต้องการแก้ไขปัญหานี้ คุณสามารถบอก RecyclerView ว่ามีอะไรเปลี่ยนแปลงบ้าง จากนั้น RecyclerView จะอัปเดตได้เฉพาะมุมมองที่เปลี่ยนบนหน้าจอเท่านั้น

RecyclerView มี API แบบสมบูรณ์สําหรับการอัปเดตองค์ประกอบเดียว คุณอาจใช้ notifyItemChanged() เพื่อแจ้ง RecyclerView ว่ารายการมีการเปลี่ยนแปลง และใช้ฟังก์ชันที่คล้ายกันสําหรับรายการที่เพิ่ม นําออก หรือย้ายแล้วได้ โดยคุณอาจทําด้วยตนเองทั้งหมด แต่งานนั้นอาจไม่ใช่เรื่องเล็กๆ และอาจใช้โค้ดเพียงเล็กน้อย

โชคดีที่ยังมีวิธีที่ดีกว่า

DiffUtil มีประสิทธิภาพและทํางานอย่างหนักเพื่อคุณ

RecyclerView มีคลาสชื่อ DiffUtil ซึ่งมีไว้สําหรับการคํานวณความแตกต่างระหว่าง 2 รายการ DiffUtil ใช้รายการเก่าและรายการใหม่เพื่อดูสิ่งที่ต่างไปจากเดิม ซึ่งจะค้นหารายการที่เพิ่ม นําออก หรือเปลี่ยนแปลง จากนั้นจะใช้อัลกอริทึมที่เรียกว่า Eugene W. Myers'อัลกอริทึมอัลกอริทึมเพื่อหาจํานวนการเปลี่ยนแปลงขั้นต่ําจากรายการเก่าเพื่อสร้างรายการใหม่

เมื่อ DiffUtil ทราบถึงสิ่งที่เปลี่ยนแปลงแล้ว RecyclerView จะใช้ข้อมูลนั้นเพื่ออัปเดตเฉพาะรายการที่เปลี่ยนแปลง เพิ่ม นําออก หรือย้าย ซึ่งมีประสิทธิภาพมากกว่าการทําซ้ําทั้งรายการ

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

ขั้นตอนที่ 1: ใช้ SleepNightDiffCallback

หากต้องการใช้ฟังก์ชันของชั้นเรียน DiffUtil ให้ขยาย DiffUtil.ItemCallback

  1. เปิด SleepNightAdapter.kt
  2. ด้านล่างคําจํากัดความชั้นเรียนแบบเต็มของ SleepNightAdapter โปรดสร้างชั้นเรียนระดับบนสุดใหม่ชื่อ SleepNightDiffCallback ที่ขยาย DiffUtil.ItemCallback ส่ง SleepNight เป็นพารามิเตอร์ทั่วไป
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
  1. วางเคอร์เซอร์ในชื่อชั้นเรียน SleepNightDiffCallback
  2. กด Alt+Enter (Option+Enter ใน Mac) แล้วเลือกใช้สมาชิก
  3. ในกล่องโต้ตอบที่เปิดขึ้น ให้กด Shift-คลิกซ้ายเพื่อเลือกเมธอด areItemsTheSame() และ areContentsTheSame() จากนั้นคลิกตกลง

    การดําเนินการนี้จะสร้างตัวยึดตําแหน่งใน SleepNightDiffCallback สําหรับ 2 วิธีต่อไปนี้ DiffUtil จะใช้ 2 วิธีนี้เพื่อให้ทราบว่ารายการและลิสต์เปลี่ยนแปลงไปอย่างไร
    override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
  1. ภายใน areItemsTheSame() ให้แทนที่ TODO ด้วยรหัสที่ทดสอบว่าทั้ง SleepNight ที่ผ่านและผ่านมา, oldItem และ newItem เหมือนกันหรือไม่ หากสินค้ามี nightId เหมือนกัน สินค้าจะเป็นสินค้าเดียวกัน ดังนั้นโปรดส่งคืน true ไม่เช่นนั้น ให้คืนสินค้า false DiffUtil ใช้การทดสอบนี้เพื่อช่วยตรวจสอบว่ามีการเพิ่ม นําออก หรือย้ายรายการหรือไม่
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem.nightId == newItem.nightId
}
  1. ภายใน areContentsTheSame() ให้ตรวจสอบว่า oldItem และ newItem มีข้อมูลเดียวกันหรือไม่ ซึ่งก็คือความเท่ากัน การตรวจสอบความเท่าเทียมกันนี้จะตรวจสอบทุกช่อง เนื่องจาก SleepNight เป็นคลาสข้อมูล Data ชั้นเรียนจะกําหนด equals และวิธีการอื่นๆ ให้คุณโดยอัตโนมัติ หากมีความแตกต่างระหว่าง oldItem และ newItem โค้ดนี้จะแจ้ง DiffUtil ว่ารายการได้รับการอัปเดตแล้ว
override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem == newItem
}

รูปแบบทั่วไปคือการใช้ RecyclerView เพื่อแสดงรายการที่มีการเปลี่ยนแปลง RecyclerView มีคลาสอะแดปเตอร์ ListAdapter ที่ช่วยให้คุณสร้างอะแดปเตอร์ RecyclerView ที่รายการรองรับได้

ListAdapter จะติดตามรายการให้คุณและแจ้งเตือนอะแดปเตอร์เมื่ออัปเดตรายการ

ขั้นตอนที่ 1: เปลี่ยนอะแดปเตอร์เพื่อขยาย ListAdapter

  1. ในไฟล์ SleepNightAdapter.kt ให้เปลี่ยนลายเซ็นของชั้นเรียน SleepNightAdapter เพื่อขยาย ListAdapter
  2. หากมีข้อความแจ้ง ให้นําเข้า androidx.recyclerview.widget.ListAdapter
  3. เพิ่ม SleepNight เป็นอาร์กิวเมนต์แรกใน ListAdapter ก่อน SleepNightAdapter.ViewHolder
  4. เพิ่ม SleepNightDiffCallback() เป็นพารามิเตอร์ให้กับเครื่องมือสร้าง ListAdapter จะใช้ค่านี้เพื่อดูสิ่งที่เปลี่ยนแปลงไปในรายการ ลายเซ็นของชั้นเรียน SleepNightAdapter ที่เสร็จสิ้นแล้วควรจะมีลักษณะดังที่แสดงด้านล่าง
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
  1. ภายในช่อง SleepNightAdapter ให้ลบช่อง data รวมถึงตัวตั้งค่าด้วย คุณไม่จําเป็นต้องใช้แล้ว เนื่องจาก ListAdapter ติดตามรายการให้คุณ
  2. ลบการลบล้าง getItemCount() เนื่องจาก ListAdapter ใช้วิธีนี้ให้คุณ
  3. หากต้องการกําจัดข้อผิดพลาดใน onBindViewHolder() ให้เปลี่ยนตัวแปร item แทนที่จะใช้ data ในการรับ item โปรดเรียกใช้เมธอด getItem(position) ที่ ListAdapter
val item = getItem(position)

ขั้นตอนที่ 2: ใช้ sendList() เพื่ออัปเดตรายการอยู่เสมอ

รหัสต้องแจ้ง ListAdapter เมื่อมีรายการที่เปลี่ยนแปลง ListAdapter ใช้วิธีการที่เรียกว่า submitList() เพื่อแจ้ง ListAdapter ว่ามีรายการเวอร์ชันใหม่ให้ใช้งาน เมื่อเรียกใช้เมธอดนี้ ListAdapter จะแยกรายการใหม่กับข้อมูลเก่า และตรวจพบรายการที่เพิ่มเข้ามา นําออก ย้าย หรือเปลี่ยนแปลง จากนั้น ListAdapter จะอัปเดตรายการที่แสดงโดย RecyclerView

  1. เปิด SleepTrackerFragment.kt
  2. ใน onCreateView() ในการสังเกตการณ์บน sleepTrackerViewModel ให้หาข้อผิดพลาดที่มีการอ้างอิงตัวแปร data ที่คุณได้ลบไว้
  3. แทนที่ adapter.data = it ด้วยการเรียก adapter.submitList(it) รหัสที่อัปเดตจะแสดงอยู่ด้านล่าง

sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.submitList(it)
   }
})
  1. เรียกใช้แอป ทํางานได้เร็วขึ้น อาจสังเกตได้ยากหากรายการมีขนาดเล็ก

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

ขั้นตอนที่ 1: เพิ่มการเชื่อมโยงข้อมูลไปยังไฟล์เลย์เอาต์

  1. เปิดไฟล์เลย์เอาต์ list_item_sleep_night.xml ในแท็บ Text
  2. วางเคอร์เซอร์ในแท็ก ConstraintLayout แล้วกด Alt+Enter (Option+Enter ใน Mac) เมนู Intent (เมนู "Quick Fix") จะเปิดขึ้น
  3. เลือกแปลงเป็นเลย์เอาต์การเชื่อมโยงข้อมูล การดําเนินการนี้จะรวมเลย์เอาต์ไว้ใน <layout> และเพิ่มแท็ก <data> ด้านใน
  4. ที่ด้านบนสุด หากจําเป็น ให้ประกาศตัวแปรชื่อ sleep ภายในแท็ก <data>
  5. ตั้ง type เป็นชื่อที่ตรงตามเกณฑ์ทั้งหมดของ SleepNight ซึ่งก็คือ com.example.android.trackmysleepquality.database.SleepNight แท็ก <data> ที่สร้างเสร็จแล้วควรมีลักษณะดังที่แสดงด้านล่างนี้
   <data>
        <variable
            name="sleep"
            type="com.example.android.trackmysleepquality.database.SleepNight"/>
    </data>
  1. หากต้องการบังคับให้สร้างออบเจ็กต์ Binding ให้เลือก Build > Clean Project แล้วเลือก Build > Rebuild Project (หากยังพบปัญหาอยู่ ให้เลือก File > Validation Caches / Restart) ระบบจะเพิ่มออบเจ็กต์การเชื่อมโยง ListItemSleepNightBinding และโค้ดที่เกี่ยวข้องลงในไฟล์ที่สร้างของโปรเจ็กต์

ขั้นตอนที่ 2: จัดวางเลย์เอาต์สินค้าให้สูงเกินจริงโดยใช้การเชื่อมโยงข้อมูล

  1. เปิด SleepNightAdapter.kt
  2. ค้นหาเมธอด from() ในคลาส ViewHolder
  3. ลบการประกาศตัวแปร view

โค้ดสําหรับลบ:

val view = layoutInflater
       .inflate(R.layout.list_item_sleep_night, parent, false)
  1. เมื่อตัวแปร view อยู่ ให้กําหนดตัวแปรใหม่ชื่อ binding ที่สูงเกินจริงออบเจ็กต์การเชื่อมโยง ListItemSleepNightBinding ดังที่แสดงด้านล่าง ทําการนําเข้าออบเจ็กต์การเชื่อมโยงที่จําเป็น
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
  1. ในตอนท้ายของฟังก์ชัน แทนที่จะส่งคืน view จะส่งคืน binding
return ViewHolder(binding)
  1. วางเคอร์เซอร์บนคําว่า binding เพื่อกําจัดข้อผิดพลาด กด Alt+Enter (Option+Enter บน Mac) เพื่อเปิดเมนู Intent
  1. เลือกเปลี่ยนพารามิเตอร์ 'itemView' ประเภทตัวสร้างหลักของคลาส 'Viewholder' เป็น 'ListItemSleepNightBinding' การดําเนินการนี้จะอัปเดตประเภทพารามิเตอร์ของคลาส ViewHolder

  1. เลื่อนขึ้นไปที่คําจํากัดความของคลาสของ ViewHolder เพื่อดูการเปลี่ยนแปลงในลายเซ็น คุณพบข้อผิดพลาด itemView เนื่องจากได้เปลี่ยน itemView เป็น binding ในเมธอด from()

    ในคํานิยามของชั้นเรียน ViewHolder คลิกขวาที่หนึ่งใน itemView ที่มีอยู่ แล้วเลือก Rector > เปลี่ยนชื่อ เปลี่ยนชื่อเป็น binding
  2. ใส่คํานําหน้าพารามิเตอร์ binding ด้วย val เป็นพร็อพเพอร์ตี้
  3. ในการโทรไปยังคลาสระดับบนสุด RecyclerView.ViewHolder ให้เปลี่ยนพารามิเตอร์จาก binding เป็น binding.root คุณต้องส่ง View และ binding.root เป็นรูท ConstraintLayout ในเลย์เอาต์ของรายการของคุณ
  4. การประกาศชั้นเรียนที่เสร็จสิ้นแล้วควรจะเป็นโค้ดด้านล่าง
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){

คุณยังพบข้อผิดพลาดในการเรียกใช้ findViewById() และจะแก้ไขในขั้นตอนต่อไป

ขั้นตอนที่ 3: แทนที่ FindViewById()

คุณสามารถอัปเดตพร็อพเพอร์ตี้ sleepLength, quality และ qualityImage เพื่อใช้ออบเจ็กต์ binding แทน findViewById() ได้แล้ว

  1. เปลี่ยนการเริ่มต้นของ sleepLength, qualityString และ qualityImage เพื่อใช้มุมมองของออบเจ็กต์ binding ดังที่แสดงด้านล่าง หลังจากนั้น โค้ดของคุณไม่ควรแสดงข้อผิดพลาดใดๆ
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage

เมื่อใช้ออบเจ็กต์การเชื่อมโยงแล้ว คุณไม่จําเป็นต้องกําหนดพร็อพเพอร์ตี้ sleepLength, quality และ qualityImage อีกต่อไป DataBinding จะแคชการค้นหา จึงไม่จําเป็นต้องประกาศพร็อพเพอร์ตี้เหล่านี้

  1. คลิกขวาที่ชื่อพร็อพเพอร์ตี้ sleepLength, quality และ qualityImage เลือก Rector > Inline หรือกด Control+Command+N (Option+Command+N บน Mac)
  2. เรียกใช้แอป (คุณอาจต้องล้างและสร้างโปรเจ็กต์อีกครั้งหากมีข้อผิดพลาด)

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

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

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

ขั้นตอนที่ 1: สร้างอะแดปเตอร์การเชื่อมโยง

โปรดทราบว่าคุณจะต้องนําเข้าชั้นเรียนจํานวนหนึ่งในขั้นตอนดังกล่าว และระบบจะไม่เรียกใช้ทีละชั้นเรียน

  1. เปิด SleepNightAdapater.kt
  2. ภายในคลาส ViewHolder ให้ค้นหาเมธอด bind() และเตือนตัวเองว่าวิธีนี้ใช้ทําอะไร คุณจะได้นําโค้ดที่คํานวณค่าสําหรับ binding.sleepLength, binding.quality และ binding.qualityImage แล้วใช้ภายในอะแดปเตอร์แทน (สําหรับตอนนี้ ให้ปล่อยโค้ดไว้ตามเดิมเพราะคุณย้ายโค้ดไปในภายหลัง)
  3. ในแพ็กเกจ sleeptracker ให้สร้างและเปิดไฟล์ชื่อ BindingUtils.kt
  4. ประกาศฟังก์ชันส่วนขยายใน TextView ชื่อ setSleepDurationFormatted และส่งใน SleepNight ฟังก์ชันนี้จะเป็นอะแดปเตอร์สําหรับคํานวณและจัดรูปแบบระยะเวลาการนอนหลับ
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
  1. ในเนื้อหาของ setSleepDurationFormatted ให้เชื่อมโยงข้อมูลกับข้อมูลพร็อพเพอร์ตี้เช่นเดียวกับใน ViewHolder.bind() เรียก convertDurationToFormatted() แล้วตั้งค่า text ของ TextView ให้เป็นข้อความที่จัดรูปแบบ (เนื่องจากนี่เป็นฟังก์ชันส่วนขยายใน TextView คุณจึงเข้าถึงพร็อพเพอร์ตี้ text ได้โดยตรง)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
  1. หากต้องการแจ้งการเชื่อมโยงข้อมูลเกี่ยวกับอะแดปเตอร์การเชื่อมโยงนี้ ให้ใส่คําอธิบายประกอบ @BindingAdapter ลงในฟังก์ชัน
  2. ฟังก์ชันนี้คืออะแดปเตอร์สําหรับแอตทริบิวต์ sleepDurationFormatted ดังนั้นโปรดส่ง sleepDurationFormatted เป็นอาร์กิวเมนต์ใน @BindingAdapter
@BindingAdapter("sleepDurationFormatted")
  1. อะแดปเตอร์ที่ 2 จะกําหนดคุณภาพการนอนหลับโดยอิงตามค่าในออบเจ็กต์ SleepNight สร้างฟังก์ชันส่วนขยายชื่อ setSleepQualityString() ใน TextView แล้วส่งใน SleepNight
  2. เชื่อมโยงข้อมูลกับข้อมูลพร็อพเพอร์ตี้เช่นเดียวกับใน ViewHolder.bind() โทร convertNumericQualityToString และตั้งค่าtext
  3. เพิ่มคําอธิบายประกอบให้กับฟังก์ชันด้วย @BindingAdapter("sleepQualityString")
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
   text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
  1. อะแดปเตอร์การเชื่อมโยงที่ 3 จะตั้งค่ารูปภาพในมุมมองรูปภาพ สร้างฟังก์ชันส่วนขยายใน ImageView โทรหา setSleepImage และใช้รหัสจาก ViewHolder.bind() ดังที่แสดงด้านล่าง
@BindingAdapter("sleepImage")
fun ImageView.setSleepImage(item: SleepNight) {
   setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

ขั้นตอนที่ 2: อัปเดต SleepNightAdapter

  1. เปิด SleepNightAdapter.kt
  2. ลบทุกอย่างในเมธอด bind() เพราะตอนนี้คุณใช้การเชื่อมโยงข้อมูลและอะแดปเตอร์ใหม่เพื่อการดําเนินการนี้ให้คุณได้
fun bind(item: SleepNight) {
}
  1. ให้สิทธิ์ bind() ภายใน item เพราะคุณต้องแจ้งออบเจ็กต์การเชื่อมโยงเกี่ยวกับ SleepNight ใหม่
binding.sleep = item
  1. เพิ่ม binding.executePendingBindings() ด้านล่างบรรทัดนั้น การเรียกนี้เป็นการเพิ่มประสิทธิภาพที่ขอให้เชื่อมโยงข้อมูลเพื่อเรียกใช้การเชื่อมโยงที่รอดําเนินการทันที การเรียกใช้ executePendingBindings() เสมอเมื่อใช้อะแดปเตอร์การเชื่อมโยงใน RecyclerView เป็นความคิดที่ดี เนื่องจากสามารถปรับขนาดมุมมองได้เล็กน้อย
 binding.executePendingBindings()

ขั้นตอนที่ 3: เพิ่มการเชื่อมโยงลงในเลย์เอาต์ XML

  1. เปิด list_item_sleep_night.xml
  2. ใน ImageView ให้เพิ่มพร็อพเพอร์ตี้ app ที่มีชื่อเดียวกันกับอะแดปเตอร์การเชื่อมโยงที่ตั้งค่ารูปภาพ ส่งผ่านตัวแปร sleep ดังที่แสดงด้านล่าง

    พร็อพเพอร์ตี้นี้จะสร้างการเชื่อมต่อระหว่างข้อมูลพร็อพเพอร์ตี้และออบเจ็กต์การเชื่อมโยงผ่านอะแดปเตอร์ เมื่อใดก็ตามที่มีการอ้างอิง sleepImage อะแดปเตอร์จะปรับข้อมูลจาก SleepNight
app:sleepImage="@{sleep}"
  1. ทําแบบเดียวกันนี้สําหรับมุมมองข้อความ sleep_length และ quality_string เมื่อใดก็ตามที่มีการอ้างอิง sleepDurationFormatted หรือ sleepQualityString อะแดปเตอร์จะปรับข้อมูลจาก SleepNight
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
  1. เรียกใช้แอป และทํางานเหมือนที่เคยทําก่อนหน้านี้ อะแดปเตอร์การเชื่อมโยงจะดูแลงานการจัดรูปแบบและการอัปเดตมุมมองทั้งหมดขณะข้อมูลมีการเปลี่ยนแปลง การปรับให้ ViewHolder ง่ายขึ้น และให้โครงสร้างโค้ดที่ดีกว่าเดิมมาก

คุณแสดงรายการของแบบฝึกหัดล่าสุด 2-3 รายการ ซึ่งได้รับการออกแบบให้แสดงให้เห็นว่าอินเทอร์เฟซ Adapter ช่วยให้คุณสถาปัตยกรรมโค้ดได้หลายวิธี โค้ดยิ่งมีความซับซ้อนมากเท่าไร ยิ่งต้องออกแบบสถาปัตยกรรมก็ยิ่งดีเท่านั้น ในแอปเวอร์ชันที่ใช้งานจริง รูปแบบเหล่านี้และอื่นๆ จะใช้กับ RecyclerView รูปแบบทั้งหมดต่างก็ใช้ได้ผล และแต่ละรูปแบบต่างก็มีประโยชน์ การเลือกได้ว่าจะสร้างแบบใดนั้นขึ้นอยู่กับสิ่งที่คุณสร้าง

ยินดีด้วย ตอนนี้คุณกําลังจะได้ฝึกหัด RecyclerView บน Android อย่างเชี่ยวชาญแล้ว

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

DiffUtil:

  • RecyclerView มีคลาสชื่อ DiffUtil ซึ่งมีไว้สําหรับการคํานวณความแตกต่างระหว่าง 2 รายการ
  • DiffUtil มีคลาสชื่อว่า ItemCallBack ที่คุณขยายเพื่อดูความแตกต่างระหว่าง 2 รายการ
  • ในชั้นเรียน ItemCallback คุณต้องลบล้างเมธอด areItemsTheSame() และ areContentsTheSame()

ListAdapter:

  • หากต้องการผู้จัดการบางรายการฟรี คุณสามารถใช้ชั้นเรียน ListAdapter แทน RecyclerView.Adapter อย่างไรก็ตาม หากใช้ ListAdapter คุณต้องเขียนอะแดปเตอร์สําหรับเลย์เอาต์อื่นๆ โดยเฉพาะ ซึ่งเป็นเหตุผลที่ Codelab นี้จะแสดงวิธีดําเนินการดังกล่าว
  • หากต้องการเปิดเมนู Intent ใน Android Studio ให้วางเคอร์เซอร์บนรายการโค้ดใดก็ได้ แล้วกด Alt+Enter (Option+Enter บน Mac) เมนูนี้มีประโยชน์อย่างยิ่งสําหรับการรีแฟคเตอร์โค้ดและสร้างต้นขั้วสําหรับวิธีการต่างๆ เมนูนี้คํานึงถึงบริบท ดังนั้น คุณจึงต้องวางเคอร์เซอร์ตรงเพื่อรับเมนูที่ถูกต้อง

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

  • ใช้การเชื่อมโยงข้อมูลในเลย์เอาต์รายการเพื่อเชื่อมโยงข้อมูลกับข้อมูลพร็อพเพอร์ตี้

อะแดปเตอร์การเชื่อมโยง:

  • ก่อนหน้านี้คุณใช้ Transformations เพื่อสร้างสตริงจากข้อมูล หากต้องการเชื่อมโยงข้อมูลประเภทต่างๆ หรือที่ซับซ้อน ให้ระบุอะแดปเตอร์การเชื่อมโยงเพื่อช่วยให้เชื่อมโยงข้อมูลได้
  • หากต้องการประกาศอะแดปเตอร์การเชื่อมโยง ให้กําหนดวิธีที่ใช้รายการและมุมมอง รวมถึงใส่คําอธิบายประกอบเมธอดด้วย @BindingAdapter ใน Kotlin คุณเขียนอะแดปเตอร์การเชื่อมโยงเป็นฟังก์ชันส่วนขยายใน View ได้ ส่งชื่อของพร็อพเพอร์ตี้ที่อะแดปเตอร์จะปรับ เช่น
@BindingAdapter("sleepDurationFormatted")
  • ในเลย์เอาต์ XML ให้ตั้งค่าพร็อพเพอร์ตี้ app โดยใช้ชื่อเดียวกับอะแดปเตอร์การเชื่อมโยง ส่งตัวแปรด้วยข้อมูล เช่น
.app:sleepDurationFormatted="@{sleep}"

หลักสูตร Udacity:

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

แหล่งข้อมูลอื่นๆ

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

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

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

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

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

คำถามที่ 1

ข้อใดต่อไปนี้จําเป็นในการใช้ DiffUtil เลือกได้มากกว่า 1 ข้อ

▢ ขยายคลาส ItemCallBack

▢ ลบล้าง areItemsTheSame()

▢ ลบล้าง areContentsTheSame()

▢ ใช้การเชื่อมโยงข้อมูลเพื่อติดตามความแตกต่างระหว่างรายการต่างๆ

คําถามที่ 2 2

ข้อใดต่อไปนี้เป็นจริงเกี่ยวกับอะแดปเตอร์การเชื่อมโยง

▢ อะแดปเตอร์การเชื่อมโยงคือฟังก์ชันที่มีคําอธิบายประกอบกับ @BindingAdapter

▢ การใช้อะแดปเตอร์การเชื่อมโยงช่วยให้คุณแยกการจัดรูปแบบข้อมูลออกจากผู้ถือมุมมองได้

▢ คุณจะต้องใช้ RecyclerViewAdapter หากต้องการใช้อะแดปเตอร์สําหรับการเชื่อมโยง

▢ การเชื่อมโยงอะแดปเตอร์ เป็นคําตอบที่ดีเมื่อคุณต้องการเปลี่ยนรูปแบบข้อมูลที่ซับซ้อน

คําถาม 3

คุณควรพิจารณาใช้ Transformations แทนอะแดปเตอร์การเชื่อมโยงเมื่อใด เลือกได้มากกว่า 1 ข้อ

▢ ข้อมูลของคุณเป็นเรื่องง่าย

▢ คุณกําลังจัดรูปแบบสตริง

▢ รายการของคุณยาวมาก

ViewHolder ของคุณมีมุมมองเพียงครั้งเดียว

เริ่มบทเรียนถัดไป: 7.3: GridLayout ด้วย RecyclerView