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ไม่เช่นนั้น ให้แสดงผลfalseDiffUtilใช้การทดสอบนี้เพื่อช่วยตรวจสอบว่ามีการเพิ่ม นำออก หรือย้ายรายการหรือไม่
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
RecyclerViewDiffUtil- ไลบรารีการเชื่อมโยงข้อมูล
- การเชื่อมโยงอะแดปเตอร์
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 มีเฉพาะมุมมองเดียว
เริ่มบทเรียนถัดไป:

