Android Kotlin Fundamentals 07.1: ข้อมูลพื้นฐานเกี่ยวกับ RecyclerView

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

ข้อมูลเบื้องต้น

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

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

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

  • การสร้างอินเทอร์เฟซผู้ใช้ (UI) พื้นฐานโดยใช้กิจกรรม ส่วนย่อย และข้อมูลพร็อพเพอร์ตี้
  • ไปยังส่วนต่างๆ ระหว่างส่วนย่อยและใช้ safeArgs เพื่อส่งข้อมูลระหว่างส่วนย่อย
  • การใช้โมเดลมุมมอง ดูโรงงานของโมเดล การเปลี่ยนรูปแบบ และ LiveData และการสังเกตการณ์
  • การสร้างฐานข้อมูล Room การสร้าง DAO และการกําหนดเอนทิตี
  • การใช้ Coroutine สําหรับงานฐานข้อมูลและงานอื่นๆ ที่ใช้เวลานาน

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

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

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

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

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

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

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

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

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

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

Android มีวิดเจ็ต RecyclerView เพื่อรองรับ Use Case เหล่านี้ทั้งหมด

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

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

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

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

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

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

การใช้ RecyclerView

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

  • ข้อมูลที่จะแสดง
  • อินสแตนซ์ RecyclerView ที่กําหนดไว้ในไฟล์เลย์เอาต์เพื่อทําหน้าที่เป็นคอนเทนเนอร์สําหรับมุมมอง
  • เลย์เอาต์ของข้อมูล 1 รายการ
    หากรายการสิ่งของทั้งหมดดูเหมือนกัน คุณจะใช้เลย์เอาต์เดียวกันสําหรับแต่ละรายการได้ แต่ไม่บังคับ คุณต้องสร้างเลย์เอาต์รายการแยกจากเลย์เอาต์ส่วนย่อยเพื่อให้สร้างมุมมองรายการได้ครั้งละ 1 รายการและกรอกข้อมูล
  • เครื่องมือจัดการเลย์เอาต์
    ตัวจัดการเลย์เอาต์จะจัดการองค์กร (เลย์เอาต์) ของคอมโพเนนต์ UI ในข้อมูลพร็อพเพอร์ตี้
  • ผู้ถือครองมุมมอง
    ผู้ถือมุมมองจะขยายชั้น ViewHolder โดยจะมีข้อมูลมุมมองสําหรับการแสดงสินค้า 1 รายการจากเลย์เอาต์ของสินค้า นอกจากนี้ เจ้าของข้อมูลพร็อพเพอร์ตี้ยังใส่ข้อมูลที่ 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 การซิงค์แอปอาจใช้เวลาสักครู่

  1. เปิดไฟล์โมดูล build.gradle เลื่อนไปที่ด้านล่าง แล้วสังเกตการขึ้นต่อกันใหม่ซึ่งดูคล้ายกับโค้ดด้านล่าง
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. ให้ id ของ sleep_list แก่ RecyclerView
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. เพิ่มผู้จัดการเลย์เอาต์ไปยัง XML ของ RecyclerView ทุก RecyclerView จําเป็นต้องมีตัวจัดการเลย์เอาต์ที่บอกวิธีวางตําแหน่งในรายการ 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() ดังที่แสดงด้านล่าง

    ฟังก์ชัน onBindViewHolder() จะเรียกใช้โดย RecyclerView เพื่อแสดงข้อมูลของ 1 รายการในตําแหน่งที่ระบุ ดังนั้น เมธอด onBindViewHolder() ใช้อาร์กิวเมนต์ 2 รายการ ได้แก่ ผู้ถือมุมมอง และตําแหน่งของข้อมูลเพื่อเชื่อมโยง สําหรับแอปนี้ เจ้าของคือ TextItemViewHolder และตําแหน่งจะอยู่ในรายการ
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
  1. ภายใน onBindViewHolder() ให้สร้างตัวแปรสําหรับสินค้า 1 รายการที่ตําแหน่งหนึ่งๆ ในข้อมูล
 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

    เครื่องมือแปลนเลย์เอาต์รู้วิธีสร้างมุมมองจากเลย์เอาต์ XML context มีข้อมูลเกี่ยวกับวิธีเพิ่มมุมมองให้สูงเกินจริง ในบริบทของมุมมองนักรีไซเคิล คุณจะต้องส่งในบริบทของกลุ่มข้อมูลพร็อพเพอร์ตี้ parent ซึ่งก็คือ 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 เมื่อข้อมูลที่แสดงอยู่มีการเปลี่ยนแปลง ให้เพิ่มตัวตั้งค่าที่กําหนดเองลงในตัวแปร data ที่อยู่ด้านบนของคลาส SleepNightAdapter เมื่อตั้งค่าใหม่ ให้ระบุ data ค่าใหม่ แล้วเรียก notifyDataSetChanged() เพื่อเรียกรายการใหม่ด้วยข้อมูลใหม่
var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

ขั้นตอนที่ 4: บอก RecyclerView เกี่ยวกับอะแดปเตอร์

RecyclerView จําเป็นต้องรู้เกี่ยวกับอะแดปเตอร์เพื่อใช้รับผู้ถือข้อมูลพร็อพเพอร์ตี้

  1. เปิด SleepTrackerFragment.kt
  2. สร้างอะแดปเตอร์ใน onCreateview() วางโค้ดนี้หลังจากสร้างโมเดล 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 เนื่องจากคุณจะสร้างการสังเกตการณ์ซึ่งจําเป็นต้องเข้าถึงตัวแปรนี้ ประกาศของคุณควรมีลักษณะเช่นนี้
val nights = database.getAllNights()
  1. ในแพ็กเกจ database ให้เปิด SleepDatabaseDao
  2. ค้นหาฟังก์ชัน getAllNights() โปรดสังเกตว่าฟังก์ชันนี้ส่งคืนรายการค่า SleepNight เป็น LiveData ซึ่งหมายความว่าตัวแปร nights ประกอบด้วย LiveData ที่ Room อัปเดตไว้ และสังเกต nights ได้ว่ามีการเปลี่ยนแปลงเมื่อใด
  3. เปิด SleepTrackerFragment
  4. ใน onCreateView() ด้านล่างสร้าง adapter ให้สร้างการสังเกตการณ์ในตัวแปร nights

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

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

ขั้นตอนที่ 6: สํารวจว่ามีการนําผู้ถือข้อมูลพร็อพเพอร์ตี้กลับมาใช้ใหม่อย่างไร

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

เนื่องจากเจ้าของข้อมูลพร็อพเพอร์ตี้เหล่านี้รีไซเคิลแล้ว โปรดตรวจสอบว่า onBindViewHolder() ได้ตั้งค่าหรือรีเซ็ตการปรับแต่งที่รายการก่อนหน้าอาจมีการตั้งค่าไว้ในผู้ถือการดู

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

  1. เพิ่มรหัสต่อไปนี้ไว้ที่ท้าย onBindViewHolder() ในชั้นเรียน SleepNightAdapter
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 นี้ คุณต้องเข้าถึงรูปภาพและมุมมองข้อความทั้ง 2 แบบ (คุณแปลงรหัสนี้เพื่อใช้การเชื่อมโยงข้อมูลในภายหลัง)
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. เปลี่ยนตัวแทรกเลย์เอาต์เพื่อใช้ทรัพยากรเลย์เอาต์ที่ถูกต้อง list_item_sleep_night
  3. นําการแคสต์ออกจาก TextView
  4. แทนที่จะส่งคืน TextItemViewHolder ให้แสดงผล ViewHolder

    นี่คือฟังก์ชัน 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 และยกเลิกการแสดงความคิดเห็นเกี่ยวกับโค้ดและการนําเข้าที่เกี่ยวข้อง (เลือก Code > comment with Line comments)
  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 ไปใช้แล้ว และเราได้รวบรวมไว้ด้วยกันเพื่อแสดงรายการด้วย RecyclerView Adapter

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

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

ขั้นตอนที่ 1: เปลี่ยนโครงสร้าง onBindViewholder()

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

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

    ฟังก์ชัน 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) เพื่อเปิดเมนู Intent เลือกแปลงพารามิเตอร์เป็นผู้รับเพื่อแปลงเป็นฟังก์ชันส่วนขยายที่มีลายเซ็นต่อไปนี้
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 การเพิ่ม tCPM ควรจะเกิดขึ้นใน ViewHolder

  1. ใน onCreateViewHolder() ให้เลือกโค้ดทั้งหมดในส่วนเนื้อหาของฟังก์ชัน
  2. คลิกขวา แล้วเลือก Rector > แยก > Function
  3. ตั้งชื่อฟังก์ชัน from และยอมรับพารามิเตอร์ที่แนะนํา คลิก OK
  4. วางเคอร์เซอร์ไว้ที่ชื่อฟังก์ชัน from กด Alt+Enter (Option+Enter บน Mac) เพื่อเปิดเมนู Intent
  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 เป็นตัวจัดการเลย์เอาต์ (เช่น LinearLayoutManager หรือ GridLayoutManager)

    คุณยังกําหนด LayoutManager สําหรับ RecyclerView แบบเป็นโปรแกรมได้ (เทคนิคนี้ครอบคลุมอยู่ใน Codelab ในภายหลัง)
  • เลย์เอาต์ของแต่ละรายการ
    สร้างเลย์เอาต์สําหรับข้อมูล 1 รายการในไฟล์เลย์เอาต์ XML
  • อะแดปเตอร์
    สร้างอะแดปเตอร์ที่เตรียมข้อมูลและวิธีที่ข้อมูลดังกล่าวจะปรากฏใน 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