หลักพื้นฐานของ Android Kotlin 07.1: หลักพื้นฐานของ RecyclerView

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

บทนำ

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

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

คุณควรคุ้นเคยกับสิ่งต่อไปนี้

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

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

  • วิธีใช้ RecyclerView กับ Adapter และ ViewHolder เพื่อแสดงรายการสินค้า

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

  • เปลี่ยนแอป TrackMySleepQuality จากบทเรียนก่อนหน้าให้ใช้ RecyclerView เพื่อแสดงข้อมูลคุณภาพการนอนหลับ

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

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

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

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

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

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

Android มีRecyclerViewวิดเจ็ตเพื่อรองรับกรณีการใช้งานทั้งหมดนี้

ประโยชน์ที่ยิ่งใหญ่ที่สุดของ RecyclerView คือการทำงานกับรายการขนาดใหญ่ได้อย่างมีประสิทธิภาพ

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

ในลําดับที่แสดงด้านล่าง คุณจะเห็นว่ามุมมองหนึ่งมีข้อมูลอยู่ ABC หลังจากที่มุมมองนั้นเลื่อนออกจากหน้าจอแล้ว RecyclerView จะนำมุมมองนั้นกลับมาใช้ซ้ำกับข้อมูลใหม่ XYZ

รูปแบบอะแดปเตอร์

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

รูปแบบอะแดปเตอร์ในวิศวกรรมซอฟต์แวร์ช่วยให้ออบเจ็กต์ทำงานร่วมกับ API อื่นได้ RecyclerView ใช้อะแดปเตอร์เพื่อแปลงข้อมูลแอปเป็นสิ่งที่ RecyclerView แสดงได้ โดยไม่เปลี่ยนวิธีที่แอปจัดเก็บและประมวลผลข้อมูล สำหรับแอปติดตามการนอนหลับ คุณจะสร้างอแดปเตอร์ที่แปลงข้อมูลจากRoomฐานข้อมูลเป็นรูปแบบที่ RecyclerView รู้ว่าจะแสดงอย่างไรโดยไม่ต้องเปลี่ยนViewModel

การใช้งาน RecyclerView

หากต้องการแสดงข้อมูลใน RecyclerView คุณต้องมีส่วนต่อไปนี้

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

ในงานนี้ คุณจะได้เพิ่ม RecyclerView ลงในไฟล์เลย์เอาต์และตั้งค่า Adapter เพื่อแสดงข้อมูลการนอนหลับต่อ RecyclerView

ขั้นตอนที่ 1: เพิ่ม RecyclerView ด้วย LayoutManager

ในขั้นตอนนี้ คุณจะแทนที่ ScrollView ด้วย RecyclerView ในไฟล์ fragment_sleep_tracker.xml

  1. ดาวน์โหลดแอป RecyclerViewFundamentals-Starter จาก GitHub
  2. สร้างและเรียกใช้แอป สังเกตวิธีแสดงข้อมูลเป็นข้อความธรรมดา
  3. เปิดfragment_sleep_tracker.xmlไฟล์เลย์เอาต์ในแท็บออกแบบใน Android Studio
  4. ในบานหน้าต่างแผนผังคอมโพเนนต์ ให้ลบ ScrollView การดำเนินการนี้จะลบ TextView ที่อยู่ภายใน ScrollView ด้วย
  5. ในแผงจานสี ให้เลื่อนดูรายการประเภทคอมโพเนนต์ทางด้านซ้ายเพื่อค้นหาคอนเทนเนอร์ แล้วเลือก
  6. ลาก RecyclerView จากบานหน้าต่างจานสีไปยังบานหน้าต่างแผนผังคอมโพเนนต์ วาง RecyclerView ไว้ใน ConstraintLayout

  1. หากมีกล่องโต้ตอบเปิดขึ้นมาถามว่าต้องการเพิ่มทรัพยากร Dependency หรือไม่ ให้คลิกตกลงเพื่อให้ Android Studio เพิ่มทรัพยากร Dependency recyclerview ลงในไฟล์ Gradle ซึ่งอาจใช้เวลา 2-3 วินาที จากนั้นแอปจะซิงค์

  1. เปิดไฟล์โมดูล build.gradle เลื่อนไปที่ด้านล่างสุด และจดบันทึกทรัพยากร Dependency ใหม่ซึ่งมีลักษณะคล้ายกับโค้ดด้านล่าง
implementation 'androidx.recyclerview:recyclerview:1.0.0'
  1. เปลี่ยนกลับเป็น fragment_sleep_tracker.xml
  2. ในแท็บข้อความ ให้มองหาโค้ด RecyclerView ที่แสดงด้านล่าง
<androidx.recyclerview.widget.RecyclerView
   android:layout_width="match_parent"
   android:layout_height="match_parent" />
  1. ให้ RecyclerView id จาก sleep_list
android:id="@+id/sleep_list"
  1. วาง RecyclerView ให้ครอบคลุมส่วนที่เหลือของหน้าจอภายใน ConstraintLayout โดยจำกัดด้านบนของ RecyclerView กับปุ่มเริ่ม ด้านล่างกับปุ่มล้าง และแต่ละด้านกับองค์ประกอบหลัก ตั้งค่าความกว้างและความสูงของเลย์เอาต์เป็น 0 dp ในเครื่องมือแก้ไขเลย์เอาต์หรือใน XML โดยใช้โค้ดต่อไปนี้
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@+id/clear_button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/stop_button"
  1. เพิ่ม Layout Manager ลงใน RecyclerView XML ทุก RecyclerView ต้องมี Layout Manager ที่บอกวิธีจัดตำแหน่งรายการในลิสต์ Android มี LinearLayoutManager ซึ่งโดยค่าเริ่มต้นจะจัดวางรายการในรายการแนวตั้งของแถวที่มีความกว้างเต็ม
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
  1. สลับไปที่แท็บออกแบบ แล้วสังเกตว่าข้อจำกัดที่เพิ่มเข้ามาทำให้ RecyclerView ขยายออกเพื่อเติมพื้นที่ว่าง

ขั้นตอนที่ 2: สร้างเลย์เอาต์รายการและตัวยึดมุมมองข้อความ

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

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

  1. สร้างไฟล์เลย์เอาต์ชื่อ text_item_view.xml ไม่ว่าคุณจะใช้อะไรเป็นองค์ประกอบรากก็ไม่สำคัญ เนื่องจากคุณจะแทนที่โค้ดเทมเพลต
  2. ใน text_item_view.xml ให้ลบรหัสทั้งหมดที่ระบุ
  3. เพิ่ม TextView โดยมีระยะห่างภายใน 16dp ที่จุดเริ่มต้นและจุดสิ้นสุด และขนาดข้อความ 24sp กำหนดให้ความกว้างตรงกับองค์ประกอบระดับบนสุด และความสูงครอบคลุมเนื้อหา เนื่องจากมุมมองนี้แสดงภายใน RecyclerView คุณจึงไม่จำเป็นต้องวางมุมมองภายใน ViewGroup
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:textSize="24sp"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:layout_width="match_parent"       
    android:layout_height="wrap_content" />
  1. เปิด Util.kt เลื่อนไปที่ท้ายสุดแล้วเพิ่มคำจำกัดความที่แสดงด้านล่าง ซึ่งจะสร้างTextItemViewHolderคลาส วางโค้ดไว้ที่ด้านล่างของไฟล์ หลังเครื่องหมายปีกกาปิดตัวสุดท้าย โค้ดจะอยู่ใน Util.kt เนื่องจากตัวยึดมุมมองนี้เป็นแบบชั่วคราวและคุณจะแทนที่ในภายหลัง
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
  1. หากได้รับข้อความแจ้ง ให้นำเข้า android.widget.TextView และ androidx.recyclerview.widget.RecyclerView

ขั้นตอนที่ 3: สร้าง SleepNightAdapter

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

  1. ในsleeptrackerแพ็กเกจ ให้สร้างคลาส Kotlin ใหม่ชื่อ SleepNightAdapter
  2. ทำให้คลาส SleepNightAdapter ขยาย RecyclerView.Adapter คลาสนี้ชื่อ SleepNightAdapter เนื่องจากจะแปลงออบเจ็กต์ SleepNight ให้เป็นสิ่งที่ RecyclerView ใช้ได้ อะแดปเตอร์ต้องทราบว่าจะใช้ตัวยึดมุมมองใด จึงต้องส่ง TextItemViewHolder เข้ามา นำเข้าคอมโพเนนต์ที่จำเป็นเมื่อได้รับแจ้ง จากนั้นคุณจะเห็นข้อผิดพลาดเนื่องจากมีเมธอดที่ต้องใช้ในการติดตั้งใช้งาน
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
  1. ที่ระดับบนสุดของ SleepNightAdapter ให้สร้างตัวแปร listOf SleepNight เพื่อเก็บข้อมูล
var data =  listOf<SleepNight>()
  1. ใน SleepNightAdapter ให้ลบล้าง getItemCount() เพื่อแสดงขนาดของรายการคืนที่นอนหลับใน data RecyclerView ต้องทราบจำนวนรายการที่อะแดปเตอร์มีเพื่อแสดง และจะทำได้โดยการเรียกใช้ getItemCount()
override fun getItemCount() = data.size
  1. ใน SleepNightAdapter ให้ลบล้างฟังก์ชัน onBindViewHolder() ดังที่แสดงด้านล่าง

    RecyclerView จะเรียกใช้ฟังก์ชัน onBindViewHolder() เพื่อแสดงข้อมูลสำหรับรายการในลิสต์ 1 รายการที่ตำแหน่งที่ระบุ ดังนั้นเมธอด onBindViewHolder() จึงรับอาร์กิวเมนต์ 2 รายการ ได้แก่ ตัวยึดมุมมองและตำแหน่งของข้อมูลที่จะเชื่อมโยง สำหรับแอปนี้ ตัวยึดตำแหน่งคือ TextItemViewHolder และตำแหน่งคือตำแหน่งในรายการ
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
  1. สร้างตัวแปรสำหรับรายการหนึ่งๆ ในตำแหน่งที่กำหนดในข้อมูลภายใน onBindViewHolder()
 val item = data[position]
  1. ViewHolderที่คุณสร้างมีพร็อพเพอร์ตี้ชื่อ textView ภายใน onBindViewHolder() ให้ตั้งค่า text ของ textView เป็นหมายเลขคุณภาพการนอนหลับ โค้ดนี้จะแสดงเฉพาะรายการตัวเลข แต่ตัวอย่างง่ายๆ นี้จะช่วยให้คุณเห็นว่าอแดปเตอร์นำข้อมูลไปยังตัวยึดมุมมองและไปยังหน้าจอได้อย่างไร
holder.textView.text = item.sleepQuality.toString()
  1. ใน SleepNightAdapter ให้ลบล้างและใช้ onCreateViewHolder() ซึ่งจะเรียกใช้เมื่อ RecyclerView ต้องการตัวยึดมุมมองเพื่อแสดงรายการ

    ฟังก์ชันนี้ใช้พารามิเตอร์ 2 รายการและส่งคืน ViewHolder พารามิเตอร์ parent ซึ่งเป็นกลุ่มมุมมองที่เก็บตัวยึดมุมมองจะเป็น RecyclerView เสมอ ระบบจะใช้พารามิเตอร์ viewType เมื่อมีหลายมุมมองใน RecyclerView เดียวกัน เช่น หากคุณใส่รายการมุมมองข้อความ รูปภาพ และวิดีโอทั้งหมดใน RecyclerView เดียวกัน ฟังก์ชัน onCreateViewHolder() จะต้องทราบประเภทมุมมองที่จะใช้
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
  1. ใน onCreateViewHolder() ให้สร้างอินสแตนซ์ของ LayoutInflater

    LayoutInflater รู้ว่าจะสร้าง View จากเลย์เอาต์ XML ได้อย่างไร context มีข้อมูลเกี่ยวกับวิธีขยายมุมมองอย่างถูกต้อง ในอแดปเตอร์สำหรับ RecyclerView คุณจะส่งบริบทของ parentViewGroup เสมอ ซึ่งก็คือ RecyclerView
val layoutInflater = LayoutInflater.from(parent.context)
  1. ใน onCreateViewHolder() ให้สร้าง view โดยขอให้ layoutinflater พองตัว

    ส่งเลย์เอาต์ XML สำหรับมุมมอง และกลุ่มมุมมอง parent สำหรับมุมมอง อาร์กิวเมนต์ที่ 3 ซึ่งเป็นบูลีนคือ attachToRoot อาร์กิวเมนต์นี้ต้องเป็น false เนื่องจาก RecyclerView จะเพิ่มรายการนี้ลงในลำดับชั้นของมุมมองให้คุณเมื่อถึงเวลา
val view = layoutInflater
       .inflate(R.layout.text_item_view, parent, false) as TextView
  1. ใน onCreateViewHolder() ให้ส่งคืนTextItemViewHolderที่สร้างด้วย view
return TextItemViewHolder(view)
  1. อแดปเตอร์ต้องแจ้งให้ RecyclerView ทราบเมื่อ data เปลี่ยนแปลง เนื่องจาก RecyclerView ไม่ทราบข้อมูลใดๆ

    RecyclerViewจะทราบเฉพาะตัวยึดมุมมองที่อแดปเตอร์ส่งให้เท่านั้น หากต้องการบอก RecyclerView เมื่อข้อมูลที่แสดงมีการเปลี่ยนแปลง ให้เพิ่มตัวตั้งค่าที่กำหนดเองลงในตัวแปร data ที่อยู่ด้านบนของคลาส SleepNightAdapter ในตัวตั้งค่า ให้กำหนดค่าใหม่ให้กับ data จากนั้นเรียกใช้ notifyDataSetChanged() เพื่อทริกเกอร์การวาดรายการใหม่ด้วยข้อมูลใหม่
var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

ขั้นตอนที่ 4: บอก RecyclerView เกี่ยวกับ Adapter

RecyclerView ต้องทราบเกี่ยวกับอแดปเตอร์ที่จะใช้เพื่อรับตัวยึดมุมมอง

  1. เปิด SleepTrackerFragment.kt
  2. สร้างอะแดปเตอร์ใน onCreateview() วางโค้ดนี้หลังการสร้างViewModelโมเดลViewModelและก่อนคำสั่งreturn
val adapter = SleepNightAdapter()
  1. เชื่อมโยง adapter กับ RecyclerView
binding.sleepList.adapter = adapter
  1. ล้างและสร้างโปรเจ็กต์ใหม่เพื่ออัปเดตออบเจ็กต์ binding

    หากยังพบข้อผิดพลาดเกี่ยวกับ binding.sleepList หรือ binding.FragmentSleepTrackerBinding ให้ล้างแคชและรีสตาร์ท (เลือกไฟล์ > ล้างแคช / รีสตาร์ท)

    หากเรียกใช้แอปตอนนี้จะไม่มีข้อผิดพลาด แต่คุณจะไม่เห็นข้อมูลใดๆ แสดงเมื่อแตะเริ่ม แล้วแตะหยุด

ขั้นตอนที่ 5: รับข้อมูลลงในอแดปเตอร์

ตอนนี้คุณมีอะแดปเตอร์และวิธีรับข้อมูลจากอะแดปเตอร์ไปยัง RecyclerView แล้ว ตอนนี้คุณต้องนำข้อมูลจาก ViewModel ไปยังอแดปเตอร์

  1. เปิด SleepTrackerViewModel
  2. ค้นหาตัวแปร nights ซึ่งจัดเก็บคืนที่นอนหลับทั้งหมด ซึ่งเป็นข้อมูลที่จะแสดง ระบบจะตั้งค่าตัวแปร nights โดยการเรียกใช้ getAllNights() ในฐานข้อมูล
  3. นำ private ออกจาก nights เนื่องจากคุณจะสร้าง Observer ที่ต้องเข้าถึงตัวแปรนี้ การประกาศควรมีลักษณะดังนี้
val nights = database.getAllNights()
  1. เปิด SleepDatabaseDao ในdatabaseแพ็กเกจ
  2. ค้นหาฟังก์ชัน getAllNights() โปรดทราบว่าฟังก์ชันนี้จะแสดงผลรายการค่า SleepNight เป็น LiveData ซึ่งหมายความว่าตัวแปร nights มี LiveData ที่ Room คอยอัปเดตอยู่ และคุณสามารถสังเกต nights เพื่อดูว่ามีการเปลี่ยนแปลงเมื่อใด
  3. เปิด SleepTrackerFragment
  4. ใน onCreateView() ให้สร้าง Observer ในตัวแปร nights ใต้การสร้าง adapter

    การระบุ viewLifecycleOwner ของ Fragment เป็นเจ้าของวงจรจะช่วยให้มั่นใจได้ว่า Observer นี้จะทำงานเฉพาะเมื่อ RecyclerView อยู่บนหน้าจอเท่านั้น
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   })
  1. ภายใน Observer ทุกครั้งที่คุณได้รับค่าที่ไม่ใช่ Null (สำหรับ nights) ให้กำหนดค่าให้กับ data ของอแดปเตอร์ นี่คือโค้ดที่เสร็จสมบูรณ์สำหรับ Observer และการตั้งค่าข้อมูล
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.data = it
   }
})
  1. สร้างและเรียกใช้โค้ด

    คุณจะเห็นตัวเลขคุณภาพการนอนหลับเป็นรายการ หากอะแดปเตอร์ทำงาน ภาพหน้าจอด้านซ้ายแสดง -1 หลังจากที่คุณแตะเริ่ม ภาพหน้าจอด้านขวาแสดงหมายเลขคุณภาพการนอนหลับที่อัปเดตแล้วหลังจากที่คุณแตะหยุดและเลือกคะแนนคุณภาพ

ขั้นตอนที่ 6: ดูวิธีรีไซเคิลที่ใส่ภาพ

RecyclerView รีไซเคิลตัวยึดมุมมอง ซึ่งหมายความว่าตัวยึดมุมมองจะนำกลับมาใช้ซ้ำ เมื่อมุมมองเลื่อนออกจากหน้าจอ RecyclerView จะนำมุมมองนั้นกลับมาใช้ใหม่สำหรับมุมมองที่กำลังจะเลื่อนเข้ามาในหน้าจอ

เนื่องจากมีการนำตัวยึดมุมมองเหล่านี้กลับมาใช้ใหม่ โปรดตรวจสอบว่า onBindViewHolder() ตั้งค่าหรือรีเซ็ตการปรับแต่งใดๆ ที่รายการก่อนหน้าอาจตั้งค่าไว้ในตัวยึดมุมมอง

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

  1. ในSleepNightAdapter class ให้เพิ่มโค้ดต่อไปนี้ที่ส่วนท้ายของ onBindViewHolder()
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
}
  1. เรียกใช้แอป
  2. เพิ่มข้อมูลคุณภาพการนอนหลับต่ำ แล้วตัวเลขจะเป็นสีแดง
  3. ให้คะแนนคุณภาพการนอนหลับสูงจนกว่าคุณจะเห็นตัวเลขสีแดงสูงบนหน้าจอ

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

  1. หากต้องการแก้ไขปัญหานี้ ให้เพิ่มคำสั่ง else เพื่อตั้งค่าสีเป็นดำหากคุณภาพไม่น้อยกว่าหรือเท่ากับ 1

    เมื่อระบุเงื่อนไขทั้ง 2 อย่างชัดเจน ตัวยึดมุมมองจะใช้สีข้อความที่ถูกต้องสำหรับแต่ละรายการ
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
} else {
   // reset
   holder.textView.setTextColor(Color.BLACK) // black
}
  1. เรียกใช้แอป แล้วตัวเลขควรมีสีที่ถูกต้องเสมอ

ยินดีด้วย ตอนนี้คุณมี RecyclerView พื้นฐานที่ทำงานได้อย่างเต็มรูปแบบแล้ว

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

ViewHolder ง่ายๆ ที่คุณเพิ่มลงใน Util.kt จะครอบ TextView ใน TextItemViewHolder

class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)

แล้วทำไม RecyclerView ไม่ใช้ TextView โดยตรง โค้ดบรรทัดเดียวนี้มีฟังก์ชันการทำงานมากมาย ViewHolder อธิบายมุมมองไอเทมและข้อมูลเมตาเกี่ยวกับตำแหน่งของไอเทมภายใน RecyclerView RecyclerView อาศัยฟังก์ชันนี้เพื่อจัดตำแหน่งมุมมองอย่างถูกต้องขณะที่รายการเลื่อน และเพื่อทำสิ่งต่างๆ ที่น่าสนใจ เช่น ทำให้มุมมองเคลื่อนไหวเมื่อมีการเพิ่มหรือนำรายการออกใน Adapter

หาก RecyclerView จำเป็นต้องเข้าถึงมุมมองที่จัดเก็บไว้ใน ViewHolder ก็สามารถทำได้โดยใช้พร็อพเพอร์ตี้ itemView ของผู้ถือมุมมอง RecyclerView ใช้ itemView เมื่อเชื่อมโยงรายการเพื่อแสดงบนหน้าจอ เมื่อวาดการตกแต่งรอบๆ มุมมอง เช่น เส้นขอบ และเมื่อใช้การช่วยเหลือพิเศษ

ขั้นตอนที่ 1: สร้างเลย์เอาต์ของรายการ

ในขั้นตอนนี้ คุณจะได้สร้างไฟล์เลย์เอาต์สำหรับ 1 รายการ เลย์เอาต์ประกอบด้วย ConstraintLayout ที่มี ImageView สำหรับคุณภาพการนอนหลับ TextView สำหรับระยะเวลาการนอนหลับ และ TextView สำหรับคุณภาพเป็นข้อความ เนื่องจากคุณเคยทำเลย์เอาต์มาก่อน ให้คัดลอกและวางโค้ด XML ที่ให้ไว้

  1. สร้างไฟล์ทรัพยากรเลย์เอาต์ใหม่และตั้งชื่อเป็น list_item_sleep_night
  2. แทนที่โค้ดทั้งหมดในไฟล์ด้วยโค้ดด้านล่าง จากนั้นทำความคุ้นเคยกับเลย์เอาต์ที่คุณเพิ่งสร้าง
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <ImageView
       android:id="@+id/quality_image"
       android:layout_width="@dimen/icon_size"
       android:layout_height="60dp"
       android:layout_marginStart="16dp"
       android:layout_marginTop="8dp"
       android:layout_marginBottom="8dp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       tools:srcCompat="@drawable/ic_sleep_5" />

   <TextView
       android:id="@+id/sleep_length"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginStart="8dp"
       android:layout_marginTop="8dp"
       android:layout_marginEnd="16dp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toEndOf="@+id/quality_image"
       app:layout_constraintTop_toTopOf="@+id/quality_image"
       tools:text="Wednesday" />

   <TextView
       android:id="@+id/quality_string"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginTop="8dp"
       app:layout_constraintEnd_toEndOf="@+id/sleep_length"
       app:layout_constraintHorizontal_bias="0.0"
       app:layout_constraintStart_toStartOf="@+id/sleep_length"
       app:layout_constraintTop_toBottomOf="@+id/sleep_length"
       tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>
  1. เปลี่ยนไปที่แท็บออกแบบใน Android Studio ในมุมมองการออกแบบ เลย์เอาต์จะมีลักษณะคล้ายกับภาพหน้าจอด้านซ้ายด้านล่าง ในมุมมองพิมพ์เขียว จะมีลักษณะเหมือนภาพหน้าจอด้านขวา

ขั้นตอนที่ 2: สร้าง ViewHolder

  1. เปิด SleepNightAdapter.kt
  2. สร้างคลาสภายใน SleepNightAdapter ชื่อ ViewHolder และทำให้คลาสขยาย RecyclerView.ViewHolder
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
  1. ดูข้อมูลอ้างอิงถึงมุมมองต่างๆ ได้ใน ViewHolder คุณต้องมีข้อมูลอ้างอิงถึงมุมมองที่ ViewHolder นี้จะอัปเดต ทุกครั้งที่คุณผูก ViewHolder นี้ คุณจะต้องเข้าถึงรูปภาพและทั้งมุมมองข้อความ (คุณจะแปลงโค้ดนี้เพื่อใช้การเชื่อมโยงข้อมูลในภายหลัง)
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)

ขั้นตอนที่ 3: ใช้ ViewHolder ใน SleepNightAdapter

  1. ในคำจำกัดความของ SleepNightAdapter ให้ใช้ SleepNightAdapter.ViewHolder ที่คุณเพิ่งสร้างแทน TextItemViewHolder
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {

อัปเดต onCreateViewHolder():

  1. เปลี่ยนลายเซ็นของ onCreateViewHolder() เพื่อส่งคืน ViewHolder
  2. เปลี่ยน LayoutInflator เพื่อใช้ทรัพยากรเลย์เอาต์ที่ถูกต้อง list_item_sleep_night
  3. นำการแคสต์ไปยัง TextView ออก
  4. ให้แสดงผล ViewHolder แทนที่จะแสดงผล TextItemViewHolder

    นี่คือฟังก์ชัน onCreateViewHolder() ที่อัปเดตแล้ว
    override fun onCreateViewHolder(
            parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = 
            LayoutInflater.from(parent.context)
        val view = layoutInflater
                .inflate(R.layout.list_item_sleep_night, 
                         parent, false)
        return ViewHolder(view)
    }

อัปเดต onBindViewHolder():

  1. เปลี่ยนลายเซ็นของ onBindViewHolder() เพื่อให้พารามิเตอร์ holder เป็น ViewHolder แทนที่จะเป็น TextItemViewHolder
  2. ใน onBindViewHolder() ให้ลบโค้ดทั้งหมด ยกเว้นคำจำกัดความของ item
  3. กำหนด val res ที่มีข้อมูลอ้างอิงถึง resources สำหรับมุมมองนี้
val res = holder.itemView.context.resources
  1. ตั้งค่าข้อความของมุมมองข้อความ sleepLength เป็นระยะเวลา คัดลอกโค้ดด้านล่าง ซึ่งเรียกใช้ฟังก์ชันการจัดรูปแบบที่มาพร้อมกับโค้ดเริ่มต้น
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
  1. ซึ่งจะทำให้เกิดข้อผิดพลาด เนื่องจากต้องกำหนด convertDurationToFormatted() เปิด Util.kt และยกเลิกการแสดงความคิดเห็นของโค้ดและการนำเข้าที่เกี่ยวข้อง (เลือกโค้ด > แสดงความคิดเห็นด้วยความคิดเห็นระดับบรรทัด)
  2. กลับไปที่ onBindViewHolder() แล้วใช้ convertNumericQualityToString() เพื่อตั้งค่าคุณภาพ
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
  1. คุณอาจต้องนำเข้าฟังก์ชันเหล่านี้ด้วยตนเอง
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
  1. ตั้งค่าไอคอนที่ถูกต้องสำหรับคุณภาพ เราได้จัดเตรียมไอคอน ic_sleep_active ใหม่ไว้ให้คุณในโค้ดเริ่มต้น
holder.qualityImage.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
})
  1. นี่คือฟังก์ชัน onBindViewHolder() ที่อัปเดตเสร็จแล้ว ซึ่งตั้งค่าข้อมูลทั้งหมดสำหรับ ViewHolder
   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = data[position]
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.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
        })
    }
  1. เรียกใช้แอป จอแสดงผลควรมีลักษณะเหมือนภาพหน้าจอด้านล่าง ซึ่งแสดงไอคอนคุณภาพการนอนหลับ พร้อมข้อความสำหรับระยะเวลาการนอนหลับและคุณภาพการนอนหลับ

RecyclerView เสร็จสมบูรณ์แล้ว คุณได้เรียนรู้วิธีใช้ Adapter และ ViewHolder รวมถึงนำทั้ง 2 อย่างมารวมกันเพื่อแสดงรายการที่มี RecyclerView Adapter

โค้ดของคุณจนถึงตอนนี้แสดงกระบวนการสร้างอะแดปเตอร์และตัวยึดมุมมอง แต่คุณสามารถปรับปรุงโค้ดนี้ได้ โค้ดที่ใช้แสดงและโค้ดที่ใช้จัดการตัวยึดมุมมองจะปะปนกัน และ onBindViewHolder() ทราบรายละเอียดเกี่ยวกับวิธีอัปเดต ViewHolder

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

ขั้นตอนที่ 1: ปรับโครงสร้าง onBindViewHolder()

ในขั้นตอนนี้ คุณจะปรับโครงสร้างโค้ดและย้ายฟังก์ชันทั้งหมดของตัวยึดมุมมองไปยัง ViewHolder การปรับโครงสร้างโค้ดนี้ไม่ได้มีวัตถุประสงค์เพื่อเปลี่ยนลักษณะที่แอปปรากฏต่อผู้ใช้ แต่เพื่อให้การทำงานกับโค้ดง่ายและปลอดภัยยิ่งขึ้นสำหรับนักพัฒนาแอป โชคดีที่ Android Studio มีเครื่องมือที่จะช่วยคุณได้

  1. ใน SleepNightAdapter ใน onBindViewHolder() ให้เลือกทุกอย่างยกเว้นคำสั่งเพื่อประกาศตัวแปร item
  2. คลิกขวา แล้วเลือกจัดระเบียบใหม่ > แยก > ฟังก์ชัน
  3. ตั้งชื่อฟังก์ชัน bind และยอมรับพารามิเตอร์ที่แนะนำ คลิกตกลง

    ฟังก์ชัน bind() จะอยู่ใต้ onBindViewHolder()
    private fun bind(holder: ViewHolder, item: SleepNight) {
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.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
        })
    }
  1. วางเคอร์เซอร์บนคำว่า holder ของพารามิเตอร์ holder ของ bind() กด Alt+Enter (Option+Enter ใน Mac) เพื่อเปิดเมนูความตั้งใจ เลือกแปลงพารามิเตอร์เป็นตัวรับเพื่อแปลงเป็นฟังก์ชันส่วนขยายที่มีลายเซ็นต่อไปนี้
private fun ViewHolder.bind(item: SleepNight) {...}
  1. ตัดและวางฟังก์ชัน bind() ลงใน ViewHolder
  2. ตั้งค่า bind() เป็นสาธารณะ
  3. นำเข้า bind() ลงในอะแดปเตอร์ หากจำเป็น
  4. เนื่องจากตอนนี้อยู่ใน ViewHolder แล้ว คุณจึงนำส่วน ViewHolder ของลายเซ็นออกได้ นี่คือโค้ดสุดท้ายสำหรับฟังก์ชัน bind() ในคลาส ViewHolder
fun bind(item: SleepNight) {
   val res = itemView.context.resources
   sleepLength.text = convertDurationToFormatted(
           item.startTimeMilli, item.endTimeMilli, res)
   quality.text = convertNumericQualityToString(
           item.sleepQuality, res)
   qualityImage.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: ปรับโครงสร้าง onCreateViewHolder

ขณะนี้ onCreateViewHolder() ในอแดปเตอร์จะขยายมุมมองจากแหล่งข้อมูลเลย์เอาต์สำหรับ ViewHolder อย่างไรก็ตาม เงินเฟ้อไม่เกี่ยวข้องกับอะแดปเตอร์ แต่เกี่ยวข้องกับViewHolder ภาวะเงินเฟ้อควรเกิดขึ้นใน ViewHolder

  1. ใน onCreateViewHolder() ให้เลือกโค้ดทั้งหมดในส่วนเนื้อหาของฟังก์ชัน
  2. คลิกขวา แล้วเลือกจัดระเบียบใหม่ > แยก > ฟังก์ชัน
  3. ตั้งชื่อฟังก์ชัน from และยอมรับพารามิเตอร์ที่แนะนำ คลิกตกลง
  4. วางเคอร์เซอร์บนชื่อฟังก์ชัน from กด Alt+Enter (Option+Enter ใน Mac) เพื่อเปิดเมนูความตั้งใจ
  5. เลือกย้ายไปยังออบเจ็กต์เสริม ฟังก์ชัน from() ต้องอยู่ในออบเจ็กต์คู่เพื่อให้เรียกใช้ในคลาส ViewHolder ได้ ไม่ใช่เรียกใช้ในอินสแตนซ์ ViewHolder
  6. ย้ายออบเจ็กต์ companion ไปยังคลาส ViewHolder
  7. ตั้งค่า from() เป็นสาธารณะ
  8. ใน onCreateViewHolder() ให้เปลี่ยนคำสั่ง return เพื่อแสดงผลลัพธ์ของการเรียก from() ในคลาส ViewHolder

    เมธอด onCreateViewHolder() และ from() ที่เสร็จสมบูรณ์แล้วควรมีลักษณะเหมือนโค้ดด้านล่าง และโค้ดควรสร้างและเรียกใช้ได้โดยไม่มีข้อผิดพลาด
    override fun onCreateViewHolder(parent: ViewGroup, viewType: 
Int): ViewHolder {
        return ViewHolder.from(parent)
    }
companion object {
   fun from(parent: ViewGroup): ViewHolder {
       val layoutInflater = LayoutInflater.from(parent.context)
       val view = layoutInflater
               .inflate(R.layout.list_item_sleep_night, parent, false)
       return ViewHolder(view)
   }
}
  1. เปลี่ยนลายเซ็นของคลาส ViewHolder เพื่อให้ตัวสร้างเป็นแบบส่วนตัว เนื่องจากตอนนี้ from() เป็นเมธอดที่แสดงผลอินสแตนซ์ ViewHolder ใหม่ จึงไม่มีเหตุผลที่ใครจะเรียกใช้ตัวสร้างของ ViewHolder อีกต่อไป
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
  1. เรียกใช้แอป แอปควรสร้างและเรียกใช้ได้เหมือนเดิม ซึ่งเป็นผลลัพธ์ที่ต้องการหลังจากการปรับโครงสร้าง

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

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

หากต้องการแสดงข้อมูลใน RecyclerView คุณต้องมีส่วนต่อไปนี้

  • RecyclerView
    หากต้องการสร้างอินสแตนซ์ของ RecyclerView ให้กำหนดองค์ประกอบ <RecyclerView> ในไฟล์เลย์เอาต์
  • LayoutManager
    RecyclerView ใช้ LayoutManager เพื่อจัดระเบียบเลย์เอาต์ของรายการใน RecyclerView เช่น การจัดวางในตารางกริดหรือในรายการเชิงเส้น

    ใน <RecyclerView> ในไฟล์เลย์เอาต์ ให้ตั้งค่าแอตทริบิวต์ app:layoutManager เป็น LayoutManager (เช่น LinearLayoutManager หรือ GridLayoutManager)

    คุณยังตั้งค่า LayoutManager สำหรับ RecyclerView โดยใช้โปรแกรมได้ด้วย (เราจะอธิบายเทคนิคนี้ใน Codelab ในภายหลัง)
  • เลย์เอาต์สำหรับแต่ละรายการ
    สร้างเลย์เอาต์สำหรับข้อมูล 1 รายการในไฟล์เลย์เอาต์ XML
  • Adapter
    สร้าง Adapter ที่เตรียมข้อมูลและวิธีแสดงข้อมูลใน ViewHolder เชื่อมโยงอะแดปเตอร์กับ RecyclerView

    เมื่อ RecyclerView ทำงาน ระบบจะใช้อะแดปเตอร์เพื่อดูวิธีแสดงข้อมูลบนหน้าจอ

    อะแดปเตอร์กำหนดให้คุณใช้เมธอดต่อไปนี้
    getItemCount() เพื่อแสดงจำนวนรายการ
    onCreateViewHolder() เพื่อแสดง ViewHolder สำหรับรายการในลิสต์
    onBindViewHolder() เพื่อปรับข้อมูลให้เข้ากับมุมมองสำหรับรายการในลิสต์

  • ViewHolder
    ViewHolder มีข้อมูลมุมมองสำหรับการแสดงสินค้า 1 รายการจากเลย์เอาต์ของสินค้า
  • onBindViewHolder() วิธีการในอแดปเตอร์จะปรับข้อมูลให้เข้ากับมุมมอง คุณจะลบล้างวิธีการนี้ได้เสมอ โดยปกติแล้ว onBindViewHolder() จะขยายเลย์เอาต์สำหรับรายการ และวางข้อมูลในมุมมองในเลย์เอาต์
  • เนื่องจาก RecyclerView ไม่ทราบข้อมูลใดๆ Adapter จึงต้องแจ้งให้ RecyclerView ทราบเมื่อข้อมูลมีการเปลี่ยนแปลง ใช้ notifyDataSetChanged() เพื่อแจ้งให้ Adapter ทราบว่าข้อมูลมีการเปลี่ยนแปลง

หลักสูตร Udacity:

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

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

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

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

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

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

คำถามที่ 1

RecyclerView แสดงรายการอย่างไร เลือกได้มากกว่า 1 ข้อ

▢ แสดงรายการในรูปแบบรายการหรือตารางกริด

▢ เลื่อนในแนวตั้งหรือแนวนอน

▢ เลื่อนในแนวทแยงบนอุปกรณ์ขนาดใหญ่ เช่น แท็บเล็ต

▢ อนุญาตเลย์เอาต์ที่กำหนดเองเมื่อรายการหรือตารางไม่เพียงพอสำหรับกรณีการใช้งาน

คำถามที่ 2

การใช้ RecyclerView มีประโยชน์อย่างไร เลือกได้มากกว่า 1 ข้อ

▢ แสดงรายการขนาดใหญ่อย่างมีประสิทธิภาพ

▢ อัปเดตข้อมูลโดยอัตโนมัติ

▢ ลดความจำเป็นในการรีเฟรชเมื่อมีการอัปเดต ลบ หรือเพิ่มรายการลงในลิสต์

▢ ใช้ซ้ำมุมมองที่เลื่อนออกนอกหน้าจอเพื่อแสดงรายการถัดไปที่เลื่อนบนหน้าจอ

คำถามที่ 3

เหตุผลบางประการในการใช้อะแดปเตอร์มีอะไรบ้าง เลือกได้มากกว่า 1 ข้อ

▢ การแยกความกังวลทำให้การเปลี่ยนแปลงและทดสอบโค้ดง่ายขึ้น

RecyclerView ไม่ขึ้นอยู่กับข้อมูลที่แสดง

▢ เลเยอร์การประมวลผลข้อมูลไม่จำเป็นต้องกังวลเกี่ยวกับวิธีแสดงข้อมูล

▢ แอปจะทำงานเร็วขึ้น

คำถามที่ 4

ข้อใดต่อไปนี้เป็นจริงเกี่ยวกับ ViewHolder เลือกได้มากกว่า 1 ข้อ

ViewHolderเลย์เอาต์กำหนดไว้ในไฟล์เลย์เอาต์ XML

▢ มี ViewHolder 1 รายการสําหรับข้อมูลแต่ละหน่วยในชุดข้อมูล

▢ คุณมี ViewHolder ได้มากกว่า 1 รายการใน RecyclerView

Adapter จะเชื่อมโยงข้อมูลกับ ViewHolder

เริ่มบทเรียนถัดไป: 7.2: DiffUtil และการเชื่อมโยงข้อมูลกับ RecyclerView