Codelab นี้เป็นส่วนหนึ่งของหลักสูตรหลักพื้นฐานของ Android Kotlin คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำตาม Codelab ตามลำดับ Codelab ของหลักสูตรทั้งหมดแสดงอยู่ในหน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin
บทนำ
ใน Codelab ก่อนหน้านี้ คุณได้อัปเดตแอป TrackMySleepQuality เพื่อแสดงข้อมูลเกี่ยวกับคุณภาพการนอนหลับใน RecyclerView
เทคนิคที่คุณได้เรียนรู้เมื่อสร้าง RecyclerView
ตัวแรกนั้นเพียงพอสำหรับ RecyclerViews
ส่วนใหญ่ที่แสดงรายการแบบง่ายๆ ซึ่งมีขนาดไม่ใหญ่เกินไป อย่างไรก็ตาม มีเทคนิคหลายอย่างที่ช่วยให้ RecyclerView
มีประสิทธิภาพมากขึ้นสำหรับรายการขนาดใหญ่ และทำให้โค้ดของคุณดูแลรักษาและขยายได้ง่ายขึ้นสำหรับรายการและตารางที่ซับซ้อน
ใน Codelab นี้ คุณจะสร้างแอปติดตามการนอนหลับจาก Codelab ก่อนหน้า คุณจะได้เรียนรู้วิธีที่มีประสิทธิภาพมากขึ้นในการอัปเดตรายการข้อมูลการนอนหลับ และวิธีใช้การเชื่อมโยงข้อมูลกับ RecyclerView
(หากไม่มีแอปจากโค้ดแล็บก่อนหน้า คุณสามารถดาวน์โหลดโค้ดเริ่มต้นสำหรับโค้ดแล็บนี้ได้)
สิ่งที่คุณควรทราบอยู่แล้ว
- การสร้างอินเทอร์เฟซผู้ใช้พื้นฐานโดยใช้กิจกรรม Fragment และมุมมอง
- การไปยังส่วนย่อยต่างๆ และการใช้
safeArgs
เพื่อส่งข้อมูลระหว่างส่วนย่อย - ดูโมเดล โรงงานโมเดล การแปลง และ
LiveData
รวมถึง Observer ของโมเดล - วิธีสร้าง
Room
ฐานข้อมูล สร้าง DAO และกําหนดเอนทิตี - วิธีใช้โครูทีนสำหรับฐานข้อมูลและงานอื่นๆ ที่ใช้เวลานาน
- วิธีใช้
RecyclerView
พื้นฐานกับAdapter
,ViewHolder
และเลย์เอาต์รายการ
สิ่งที่คุณจะได้เรียนรู้
- วิธีใช้
DiffUtil
เพื่ออัปเดตรายการที่แสดงโดยRecyclerView
อย่างมีประสิทธิภาพ - วิธีใช้การเชื่อมโยงข้อมูลกับ
RecyclerView
- วิธีใช้อะแดปเตอร์การเชื่อมโยงเพื่อเปลี่ยนรูปแบบข้อมูล
สิ่งที่คุณต้องดำเนินการ
- สร้างต่อจากแอป TrackMySleepQuality จาก Codelab ก่อนหน้าในชุดนี้
- อัปเดต
SleepNightAdapter
เพื่ออัปเดตรายการอย่างมีประสิทธิภาพโดยใช้DiffUtil
- ใช้การเชื่อมโยงข้อมูลสำหรับ
RecyclerView
โดยใช้อะแดปเตอร์การเชื่อมโยงเพื่อเปลี่ยนรูปแบบข้อมูล
แอปติดตามการนอนหลับมี 2 หน้าจอซึ่งแสดงด้วย Fragment ดังที่แสดงในรูปภาพด้านล่าง
หน้าจอแรกที่แสดงทางด้านซ้ายมีปุ่มสำหรับเริ่มและหยุดการติดตาม หน้าจอจะแสดงข้อมูลการนอนหลับบางส่วนของผู้ใช้ ปุ่มล้างจะลบข้อมูลทั้งหมดที่แอปเก็บรวบรวมไว้สำหรับผู้ใช้ออกอย่างถาวร หน้าจอที่ 2 ซึ่งแสดงทางด้านขวาใช้สำหรับเลือกคะแนนคุณภาพการนอนหลับ
แอปนี้ได้รับการออกแบบมาให้ใช้ตัวควบคุม UI, ViewModel
และ LiveData
รวมถึงฐานข้อมูล Room
เพื่อจัดเก็บข้อมูลการนอนหลับ
ข้อมูลการนอนหลับจะแสดงในRecyclerView
ในโค้ดแล็บนี้ คุณจะได้สร้างส่วน DiffUtil
และการเชื่อมโยงข้อมูลสำหรับ RecyclerView
หลังจากทำ Codelab นี้แล้ว แอปของคุณจะดูเหมือนเดิมทุกประการ แต่จะมีประสิทธิภาพมากขึ้น รวมถึงปรับขนาดและบำรุงรักษาได้ง่ายขึ้น
คุณใช้แอป SleepTracker จาก Codelab ก่อนหน้าต่อไปได้ หรือจะดาวน์โหลดแอป RecyclerViewDiffUtilDataBinding-Starter จาก GitHub ก็ได้
- หากจำเป็น ให้ดาวน์โหลดแอป RecyclerViewDiffUtilDataBinding-Starter จาก GitHub แล้วเปิดโปรเจ็กต์ใน Android Studio
- เรียกใช้แอป
- เปิดไฟล์
SleepNightAdapter.kt
- ตรวจสอบโค้ดเพื่อให้คุ้นเคยกับโครงสร้างของแอป โปรดดูแผนภาพด้านล่างเพื่อสรุปการใช้
RecyclerView
กับรูปแบบอะแดปเตอร์เพื่อแสดงข้อมูลการนอนหลับต่อผู้ใช้
- แอปจะสร้างรายการออบเจ็กต์
SleepNight
จากข้อมูลที่ผู้ใช้ป้อน ออบเจ็กต์SleepNight
แต่ละรายการแสดงถึงการนอนหลับ 1 คืน ระยะเวลา และคุณภาพของการนอนหลับ SleepNightAdapter
จะปรับรายการออบเจ็กต์SleepNight
ให้เป็นสิ่งที่RecyclerView
ใช้และแสดงได้SleepNightAdapter
อะแดปเตอร์จะสร้างViewHolders
ที่มีมุมมอง ข้อมูล และข้อมูลเมตาเพื่อให้ RecyclerView แสดงข้อมูลRecyclerView
ใช้SleepNightAdapter
เพื่อกำหนดจำนวนรายการที่จะแสดง (getItemCount()
)RecyclerView
ใช้onCreateViewHolder()
และonBindViewHolder()
เพื่อรับตัวยึดมุมมองที่เชื่อมโยงกับข้อมูลสำหรับการแสดง
เมธอด notifyDataSetChanged() ไม่มีประสิทธิภาพ
หากต้องการบอก 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
- เปิด
SleepNightAdapter.kt
- ใต้คำจำกัดความของคลาสแบบเต็มสำหรับ
SleepNightAdapter
ให้สร้างคลาสระดับบนสุดใหม่ชื่อSleepNightDiffCallback
ที่ขยายDiffUtil.ItemCallback
ส่งSleepNight
เป็นพารามิเตอร์ทั่วไป
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
- วางเคอร์เซอร์ใน
SleepNightDiffCallback
ชื่อชั้นเรียน - กด
Alt+Enter
(Option+Enter
ใน Mac) แล้วเลือกใช้สมาชิก - ในกล่องโต้ตอบที่เปิดขึ้น ให้กด Shift แล้วคลิกซ้ายเพื่อเลือกเมธอด
areItemsTheSame()
และareContentsTheSame()
จากนั้นคลิกตกลง
ซึ่งจะสร้าง Stub ภายใน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.
}
- ใน
areItemsTheSame()
ให้แทนที่TODO
ด้วยโค้ดที่ทดสอบว่ารายการSleepNight
ที่ส่งเข้ามา 2 รายการ ซึ่งได้แก่oldItem
และnewItem
เหมือนกันหรือไม่ หากสินค้ามีnightId
เดียวกัน แสดงว่าเป็นสินค้าเดียวกัน ให้ส่งคืนtrue
ไม่เช่นนั้น ให้แสดงผลfalse
DiffUtil
ใช้การทดสอบนี้เพื่อช่วยตรวจสอบว่ามีการเพิ่ม นำออก หรือย้ายรายการหรือไม่
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
return oldItem.nightId == newItem.nightId
}
- ใน
areContentsTheSame()
ให้ตรวจสอบว่าoldItem
และnewItem
มีข้อมูลเดียวกันหรือไม่ นั่นคือเท่ากันหรือไม่ การตรวจสอบความเท่ากันนี้จะตรวจสอบทุกฟิลด์เนื่องจากSleepNight
เป็นคลาสข้อมูลData
คลาสจะกำหนดequals
และเมธอดอื่นๆ อีก 2-3 รายการให้คุณโดยอัตโนมัติ หากoldItem
และnewItem
แตกต่างกัน โค้ดนี้จะแจ้งให้DiffUtil
ทราบว่ามีการอัปเดตรายการแล้ว
override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
return oldItem == newItem
}
รูปแบบที่ใช้กันโดยทั่วไปคือการใช้ RecyclerView
เพื่อแสดงรายการที่มีการเปลี่ยนแปลง RecyclerView
มีคลาสอะแดปเตอร์ ListAdapter
ที่ช่วยคุณสร้างอะแดปเตอร์ RecyclerView
ที่มีรายการเป็นข้อมูลสำรอง
ListAdapter
จะติดตามรายการให้คุณและแจ้งเตือนอะแดปเตอร์เมื่อมีการอัปเดตรายการ
ขั้นตอนที่ 1: เปลี่ยนอแดปเตอร์ให้ขยาย ListAdapter
- ใน
SleepNightAdapter.kt
ให้เปลี่ยนลายเซ็นของคลาสSleepNightAdapter
เพื่อขยายListAdapter
- หากได้รับข้อความแจ้ง ให้นำเข้า
androidx.recyclerview.widget.ListAdapter
- เพิ่ม
SleepNight
เป็นอาร์กิวเมนต์แรกในListAdapter
ก่อนSleepNightAdapter.ViewHolder
- เพิ่ม
SleepNightDiffCallback()
เป็นพารามิเตอร์ในเครื่องมือสร้างListAdapter
จะใช้ข้อมูลนี้เพื่อดูว่ามีการเปลี่ยนแปลงอะไรในรายการSleepNightAdapter
ลายเซ็นชั้นเรียนที่เสร็จสมบูรณ์แล้วควรมีลักษณะดังที่แสดงด้านล่าง
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
- ในคลาส
SleepNightAdapter
ให้ลบฟิลด์data
รวมถึงตัวตั้งค่า คุณไม่จำเป็นต้องใช้รายการนี้อีกต่อไป เนื่องจากListAdapter
จะติดตามรายการให้คุณ - ลบล้าง
getItemCount()
เนื่องจากListAdapter
จะใช้วิธีนี้ให้คุณ - หากต้องการแก้ไขข้อผิดพลาดใน
onBindViewHolder()
ให้เปลี่ยนตัวแปรitem
แทนที่จะใช้data
เพื่อรับitem
ให้เรียกใช้เมธอดgetItem(position)
ที่ListAdapter
จัดเตรียมไว้
val item = getItem(position)
ขั้นตอนที่ 2: ใช้ submitList() เพื่ออัปเดตรายการ
โค้ดของคุณต้องแจ้งให้ ListAdapter
ทราบเมื่อมีรายการที่เปลี่ยนแปลง ListAdapter
มีเมธอดที่เรียกว่า submitList()
เพื่อแจ้งให้ ListAdapter
ทราบว่ามีรายการเวอร์ชันใหม่พร้อมใช้งานแล้ว เมื่อเรียกใช้เมธอดนี้ ListAdapter
จะเปรียบเทียบรายการใหม่กับรายการเก่า และตรวจหารายการที่เพิ่ม นำออก ย้าย หรือเปลี่ยนแปลง จากนั้น ListAdapter
จะอัปเดตสินค้าที่ RecyclerView
แสดง
- เปิด
SleepTrackerFragment.kt
- ใน
onCreateView()
ในเครื่องมือตรวจสอบในsleepTrackerViewModel
ให้ค้นหาข้อผิดพลาดที่มีการอ้างอิงตัวแปรdata
ที่คุณลบไปแล้ว - แทนที่
adapter.data = it
ด้วยการเรียกใช้adapter.submitList(it)
โค้ดที่อัปเดตแล้วจะแสดงอยู่ด้านล่าง
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it)
}
})
- เรียกใช้แอป แอปจะทำงานเร็วขึ้น แต่คุณอาจไม่สังเกตเห็นหากรายการมีขนาดเล็ก
ในงานนี้ คุณจะได้ใช้เทคนิคเดียวกับใน Codelab ก่อนหน้าเพื่อตั้งค่าการเชื่อมโยงข้อมูล และยกเลิกการเรียกใช้ findViewById()
ขั้นตอนที่ 1: เพิ่มการเชื่อมโยงข้อมูลไปยังไฟล์เลย์เอาต์
- เปิดไฟล์เลย์เอาต์
list_item_sleep_night.xml
ในแท็บข้อความ - วางเคอร์เซอร์บนแท็ก
ConstraintLayout
แล้วกดAlt+Enter
(Option+Enter
ใน Mac) เมนูความตั้งใจ (เมนู "แก้ไขด่วน") จะเปิดขึ้น - เลือกแปลงเป็นเลย์เอาต์การเชื่อมโยงข้อมูล ซึ่งจะรวมเลย์เอาต์ไว้ใน
<layout>
และเพิ่มแท็ก<data>
ไว้ด้านใน - เลื่อนกลับไปที่ด้านบนหากจำเป็น และภายในแท็ก
<data>
ให้ประกาศตัวแปรชื่อsleep
- ตั้งค่า
type
เป็นชื่อที่สมบูรณ์ของSleepNight
,com.example.android.trackmysleepquality.database.SleepNight
แท็ก<data>
ที่เสร็จสมบูรณ์แล้วควรมีลักษณะดังที่แสดงด้านล่าง
<data>
<variable
name="sleep"
type="com.example.android.trackmysleepquality.database.SleepNight"/>
</data>
- หากต้องการบังคับให้สร้างออบเจ็กต์
Binding
ให้เลือกสร้าง > ล้างโปรเจ็กต์ แล้วเลือกสร้าง > สร้างโปรเจ็กต์ใหม่ (หากยังพบปัญหาอยู่ ให้เลือกไฟล์ > ล้างแคช / รีสตาร์ท) ระบบจะเพิ่มListItemSleepNightBinding
ออบเจ็กต์การเชื่อมโยงพร้อมกับโค้ดที่เกี่ยวข้องลงในไฟล์ที่สร้างขึ้นของโปรเจ็กต์
ขั้นตอนที่ 2: ขยายเลย์เอาต์ของรายการโดยใช้การเชื่อมโยงข้อมูล
- เปิด
SleepNightAdapter.kt
- ใน
ViewHolder
คลาส ให้ค้นหาวิธีfrom()
- ลบการประกาศตัวแปร
view
รหัสสำหรับลบ
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
- ในตำแหน่งที่มีตัวแปร
view
ให้กำหนดตัวแปรใหม่ชื่อbinding
ซึ่งจะขยายออบเจ็กต์การเชื่อมโยงListItemSleepNightBinding
ดังที่แสดงด้านล่าง นำเข้าออบเจ็กต์การเชื่อมโยงที่จำเป็น
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
- ที่ส่วนท้ายของฟังก์ชัน ให้ส่งคืน
binding
แทนที่จะส่งคืนview
return ViewHolder(binding)
- หากต้องการแก้ไขข้อผิดพลาด ให้วางเคอร์เซอร์บนคำว่า
binding
กดAlt+Enter
(Option+Enter
ใน Mac) เพื่อเปิดเมนูความตั้งใจ
- เลือกเปลี่ยนประเภทพารามิเตอร์ "itemView" ของตัวสร้างหลักของคลาส "ViewHolder" เป็น "ListItemSleepNightBinding" การดำเนินการนี้จะอัปเดตประเภทพารามิเตอร์ของคลาส
ViewHolder
- เลื่อนขึ้นไปที่คำจำกัดความของคลาสของ
ViewHolder
เพื่อดูการเปลี่ยนแปลงในลายเซ็น คุณเห็นข้อผิดพลาดสำหรับitemView
เนื่องจากคุณเปลี่ยนitemView
เป็นbinding
ในเมธอดfrom()
ในคำจำกัดความของคลาสViewHolder
ให้คลิกขวาที่อินสแตนซ์ของitemView
แล้วเลือกปรับโครงสร้าง > เปลี่ยนชื่อ เปลี่ยนชื่อเป็นbinding
- นำหน้าพารามิเตอร์ของตัวสร้าง
binding
ด้วยval
เพื่อให้เป็นพร็อพเพอร์ตี้ - ในการเรียกคลาสหลัก
RecyclerView.ViewHolder
ให้เปลี่ยนพารามิเตอร์จากbinding
เป็นbinding.root
คุณต้องส่งView
และbinding.root
คือรูทConstraintLayout
ในเลย์เอาต์ของสินค้า - การประกาศคลาสที่เสร็จสมบูรณ์แล้วควรมีลักษณะเหมือนโค้ดด้านล่าง
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){
นอกจากนี้ คุณยังเห็นข้อผิดพลาดสำหรับการเรียกไปยัง findViewById()
และคุณจะแก้ไขข้อผิดพลาดนี้ในขั้นตอนถัดไป
ขั้นตอนที่ 3: แทนที่ findViewById()
ตอนนี้คุณสามารถอัปเดตพร็อพเพอร์ตี้ sleepLength
, quality
และ qualityImage
เพื่อใช้ออบเจ็กต์ binding
แทน findViewById()
ได้แล้ว
- เปลี่ยนการเริ่มต้นของ
sleepLength
,qualityString
และqualityImage
เพื่อใช้วิวของออบเจ็กต์binding
ดังที่แสดงด้านล่าง หลังจากนี้ โค้ดของคุณไม่ควรแสดงข้อผิดพลาดอีก
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage
เมื่อมีออบเจ็กต์การเชื่อมโยงแล้ว คุณก็ไม่จำเป็นต้องกำหนดพร็อพเพอร์ตี้ sleepLength
, quality
และ qualityImage
อีกต่อไป DataBinding
จะแคชการค้นหา จึงไม่จำเป็นต้องประกาศพร็อพเพอร์ตี้เหล่านี้
- คลิกขวาที่ชื่อพร็อพเพอร์ตี้
sleepLength
,quality
และqualityImage
เลือกจัดระเบียบใหม่ > แทรก หรือกดControl+Command+N
(Option+Command+N
ใน Mac) - เรียกใช้แอป (คุณอาจต้องล้างและสร้างใหม่โปรเจ็กต์หากมีข้อผิดพลาด)
ในงานนี้ คุณจะได้อัปเกรดแอปให้ใช้การเชื่อมโยงข้อมูลกับ Binding Adapter เพื่อตั้งค่าข้อมูลใน View
ในโค้ดแล็บก่อนหน้านี้ คุณได้ใช้คลาส Transformations
เพื่อใช้ LiveData
และสร้างสตริงที่จัดรูปแบบเพื่อแสดงในมุมมองข้อความ อย่างไรก็ตาม หากต้องการเชื่อมโยงประเภทต่างๆ หรือประเภทที่ซับซ้อน คุณสามารถระบุ Binding Adapter เพื่อช่วยให้การเชื่อมโยงข้อมูลใช้ประเภทเหล่านั้นได้ Binding Adapter คือ Adapter ที่รับข้อมูลและปรับให้เป็นสิ่งที่การเชื่อมโยงข้อมูลใช้เชื่อมโยง View ได้ เช่น ข้อความหรือรูปภาพ
คุณจะใช้ Binding Adapter 3 ตัว ตัวหนึ่งสำหรับรูปภาพคุณภาพ และอีก 2 ตัวสำหรับช่องข้อความแต่ละช่อง โดยสรุปคือ หากต้องการประกาศ Binding Adapter ให้กำหนดเมธอดที่รับรายการและ View แล้วใส่คำอธิบายประกอบด้วย @BindingAdapter
ในส่วนเนื้อหาของเมธอด ให้ใช้การเปลี่ยนรูปแบบ ใน Kotlin คุณสามารถเขียน Binding Adapter เป็นฟังก์ชันส่วนขยายในคลาส View ที่รับข้อมูลได้
ขั้นตอนที่ 1: สร้าง Binding Adapter
โปรดทราบว่าคุณจะต้องนำเข้าชั้นเรียนหลายชั้นเรียนในขั้นตอนนี้ และระบบจะไม่ระบุชั้นเรียนแต่ละชั้นเรียน
- เปิด
SleepNightAdapater.kt
- ภายใน
ViewHolder
คลาส ให้ค้นหาเมธอดbind()
แล้วเตือนตัวเองว่าเมธอดนี้ทำอะไร คุณจะใช้โค้ดที่คำนวณค่าสำหรับbinding.sleepLength
,binding.quality
และbinding.qualityImage
และใช้โค้ดนั้นภายในอแดปเตอร์แทน (ตอนนี้ให้ปล่อยรหัสไว้ตามเดิม คุณจะย้ายรหัสในขั้นตอนถัดไป) - ใน
sleeptracker
แพ็กเกจ ให้สร้างและเปิดไฟล์ชื่อBindingUtils.kt
- ประกาศฟังก์ชันส่วนขยายใน
TextView
ชื่อsetSleepDurationFormatted
และส่งSleepNight
ฟังก์ชันนี้จะเป็นตัวดัดแปลงสำหรับการคำนวณและจัดรูปแบบระยะเวลาการนอนหลับ
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
- ในเนื้อหาของ
setSleepDurationFormatted
ให้เชื่อมโยงข้อมูลกับมุมมองเช่นเดียวกับที่ทำในViewHolder.bind()
เรียกใช้convertDurationToFormatted()
แล้วตั้งค่าtext
ของTextView
เป็นข้อความที่จัดรูปแบบ (เนื่องจากนี่คือฟังก์ชันส่วนขยายในTextView
คุณจึงเข้าถึงพร็อพเพอร์ตี้text
ได้โดยตรง)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
- หากต้องการบอกการเชื่อมโยงข้อมูลเกี่ยวกับอแดปเตอร์การเชื่อมโยงนี้ ให้ใส่คำอธิบายประกอบฟังก์ชันด้วย
@BindingAdapter
- ฟังก์ชันนี้เป็นตัวดัดแปลงสำหรับแอตทริบิวต์
sleepDurationFormatted
ดังนั้นให้ส่งsleepDurationFormatted
เป็นอาร์กิวเมนต์ไปยัง@BindingAdapter
@BindingAdapter("sleepDurationFormatted")
- อแดปเตอร์ที่ 2 จะตั้งค่าคุณภาพการนอนหลับตามค่าในออบเจ็กต์
SleepNight
สร้างฟังก์ชันส่วนขยายชื่อsetSleepQualityString()
ในTextView
แล้วส่งSleepNight
- ในเนื้อหา ให้เชื่อมโยงข้อมูลกับมุมมองเช่นเดียวกับที่ทำใน
ViewHolder.bind()
โทรหาconvertNumericQualityToString
แล้วตั้งtext
- ใส่คำอธิบายประกอบฟังก์ชันด้วย
@BindingAdapter("sleepQualityString")
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
- Binding Adapter ที่ 3 จะตั้งค่ารูปภาพใน ImageView สร้างฟังก์ชันส่วนขยายใน
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
- เปิด
SleepNightAdapter.kt
- ลบทุกอย่างใน
bind()
เมธอด เนื่องจากตอนนี้คุณใช้การเชื่อมโยงข้อมูลและอแดปเตอร์ใหม่เพื่อทำงานนี้แทนได้แล้ว
fun bind(item: SleepNight) {
}
- ใน
bind()
ให้กำหนดการนอนหลับเป็นitem
เนื่องจากคุณต้องแจ้งออบเจ็กต์การเชื่อมโยงเกี่ยวกับSleepNight
ใหม่
binding.sleep = item
- เพิ่ม
binding.executePendingBindings()
ใต้บรรทัดนั้น การเรียกนี้เป็นการเพิ่มประสิทธิภาพที่ขอให้การเชื่อมโยงข้อมูลดำเนินการเชื่อมโยงที่รอดำเนินการทันที การเรียกexecutePendingBindings()
เมื่อใช้ Binding Adapter ในRecyclerView
เป็นความคิดที่ดีเสมอ เนื่องจากจะช่วยเพิ่มความเร็วในการกำหนดขนาดของ View ได้เล็กน้อย
binding.executePendingBindings()
ขั้นตอนที่ 3: เพิ่มการเชื่อมโยงไปยังเลย์เอาต์ XML
- เปิด
list_item_sleep_night.xml
- ใน
ImageView
ให้เพิ่มพร็อพเพอร์ตี้app
ที่มีชื่อเดียวกันกับ Binding Adapter ที่ตั้งค่ารูปภาพ ส่งตัวแปรsleep
ดังที่แสดงด้านล่าง
พร็อพเพอร์ตี้นี้จะสร้างการเชื่อมต่อระหว่างข้อมูลพร็อพเพอร์ตี้กับออบเจ็กต์การเชื่อมโยงผ่านอแดปเตอร์ เมื่อใดก็ตามที่มีการอ้างอิงsleepImage
อะแดปเตอร์จะปรับข้อมูลจากSleepNight
app:sleepImage="@{sleep}"
- ทำเช่นเดียวกันกับมุมมองข้อความ
sleep_length
และquality_string
เมื่อใดก็ตามที่มีการอ้างอิงถึงsleepDurationFormatted
หรือsleepQualityString
อะแดปเตอร์จะปรับข้อมูลจากSleepNight
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
- เรียกใช้แอป ซึ่งจะทำงานเหมือนเดิมทุกประการ Binding Adapter จะจัดการงานทั้งหมดในการจัดรูปแบบและอัปเดต View เมื่อข้อมูลเปลี่ยนแปลง ซึ่งจะช่วยลดความซับซ้อนของ
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 นี้แสดงวิธีทำ - หากต้องการเปิดเมนูความตั้งใจใน Android Studio ให้วางเคอร์เซอร์บนรายการโค้ด แล้วกด
Alt+Enter
(Option+Enter
ใน Mac) เมนูนี้มีประโยชน์อย่างยิ่งสำหรับการปรับโครงสร้างโค้ดและการสร้าง Stub สำหรับการใช้เมธอด เมนูจะขึ้นอยู่กับบริบท ดังนั้นคุณต้องวางเคอร์เซอร์ให้ตรงตำแหน่งเพื่อรับเมนูที่ถูกต้อง
การเชื่อมโยงข้อมูล:
- ใช้การเชื่อมโยงข้อมูลในเลย์เอาต์ของรายการเพื่อเชื่อมโยงข้อมูลกับมุมมอง
การเชื่อมโยงอะแดปเตอร์
- ก่อนหน้านี้คุณใช้
Transformations
เพื่อสร้างสตริงจากข้อมูล หากต้องการเชื่อมโยงข้อมูลประเภทต่างๆ หรือข้อมูลที่ซับซ้อน ให้ระบุอะแดปเตอร์การเชื่อมโยงเพื่อช่วยให้การเชื่อมโยงข้อมูลใช้ข้อมูลเหล่านั้นได้ - หากต้องการประกาศ Binding Adapter ให้กำหนดเมธอดที่รับรายการและมุมมอง แล้วใส่คำอธิบายประกอบเมธอดด้วย
@BindingAdapter
ใน Kotlin คุณสามารถเขียน Binding Adapter เป็นฟังก์ชันส่วนขยายในView
ได้ ส่งชื่อของพร็อพเพอร์ตี้ที่อแดปเตอร์ปรับ เช่น
@BindingAdapter("sleepDurationFormatted")
- ในเลย์เอาต์ XML ให้ตั้งค่าพร็อพเพอร์ตี้
app
ที่มีชื่อเดียวกับ Binding Adapter ส่งตัวแปรพร้อมข้อมูล เช่น
.app:sleepDurationFormatted="@{sleep}"
หลักสูตรของ Udacity
เอกสารประกอบสำหรับนักพัฒนาแอป Android
- สร้างรายการด้วย RecyclerView
RecyclerView
DiffUtil
- ไลบรารีการเชื่อมโยงข้อมูล
- การเชื่อมโยงอะแดปเตอร์
notifyDataSetChanged()
Transformations
แหล่งข้อมูลอื่นๆ
ส่วนนี้แสดงรายการการบ้านที่เป็นไปได้สำหรับนักเรียน/นักศึกษาที่กำลังทำ Codelab นี้เป็นส่วนหนึ่งของหลักสูตรที่สอนโดยผู้สอน ผู้สอนมีหน้าที่ดำเนินการต่อไปนี้
- มอบหมายการบ้านหากจำเป็น
- สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานที่ได้รับมอบหมาย
- ให้คะแนนงานการบ้าน
ผู้สอนสามารถใช้คำแนะนำเหล่านี้ได้มากน้อยตามที่ต้องการ และควรมีอิสระในการมอบหมายการบ้านอื่นๆ ที่เห็นว่าเหมาะสม
หากคุณกำลังทำ Codelab นี้ด้วยตนเอง โปรดใช้แบบฝึกหัดเหล่านี้เพื่อทดสอบความรู้ของคุณ
ตอบคำถามต่อไปนี้
คำถามที่ 1
ข้อใดต่อไปนี้จำเป็นต่อการใช้ DiffUtil
เลือกได้มากกว่า 1 ข้อ
▢ ขยายItemCallBack
ชั้นเรียน
▢ ลบล้าง areItemsTheSame()
▢ ลบล้าง areContentsTheSame()
▢ ใช้การเชื่อมโยงข้อมูลเพื่อติดตามความแตกต่างระหว่างรายการ
คำถามที่ 2
ข้อใดต่อไปนี้เป็นจริงเกี่ยวกับ Binding Adapter
▢ Binding Adapter คือฟังก์ชันที่มีคำอธิบายประกอบด้วย @BindingAdapter
▢ การใช้ Binding Adapter ช่วยให้คุณแยกการจัดรูปแบบข้อมูลออกจาก View Holder ได้
▢ คุณต้องใช้ RecyclerViewAdapter
หากต้องการใช้อะแดปเตอร์สำหรับยึด
▢ อะแดปเตอร์การเชื่อมโยงเป็นโซลูชันที่ดีเมื่อคุณต้องการเปลี่ยนรูปแบบข้อมูลที่ซับซ้อน
คำถาม 3
คุณควรพิจารณาใช้ Transformations
แทนอะแดปเตอร์แบบยึดเมื่อใด เลือกได้มากกว่า 1 ข้อ
▢ ข้อมูลของคุณเรียบง่าย
▢ คุณกำลังจัดรูปแบบสตริง
▢ รายการของคุณยาวมาก
▢ ViewHolder
มีเฉพาะมุมมองเดียว
เริ่มบทเรียนถัดไป: