หลักพื้นฐานของ Android Kotlin 07.5: ส่วนหัวใน RecyclerView

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

บทนำ

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

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

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

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

  • วิธีใช้ ViewHolder มากกว่า 1 รายการกับ RecyclerView เพื่อเพิ่มรายการที่มีเลย์เอาต์ต่างกัน โดยเฉพาะวิธีใช้ ViewHolder ที่ 2 เพื่อเพิ่มส่วนหัวเหนือรายการที่แสดงใน RecyclerView

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

  • สร้างต่อจากแอป TrackMySleepQuality จาก Codelab ก่อนหน้าในชุดนี้
  • เพิ่มส่วนหัวที่ครอบคลุมความกว้างของหน้าจอเหนือคืนที่นอนหลับที่แสดงใน RecyclerView

แอปติดตามการนอนหลับที่คุณเริ่มต้นด้วยจะมี 3 หน้าจอซึ่งแสดงด้วย Fragment ดังที่แสดงในรูปด้านล่าง

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

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

ในโค้ดแล็บนี้ คุณจะเพิ่มส่วนหัวลงในตารางกริดของรายการที่แสดง หน้าจอหลักสุดท้ายจะมีลักษณะดังนี้

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

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

การเพิ่มส่วนหัวทำได้ 2 วิธี

ใน RecyclerView ทุกรายการในลิสต์จะสอดคล้องกับหมายเลขดัชนีที่เริ่มต้นจาก 0 เช่น

[ข้อมูลจริง] -> [มุมมองของอะแดปเตอร์]

[0: SleepNight] -> [0: SleepNight]

[1: SleepNight] -> [1: SleepNight]

[2: SleepNight] -> [2: SleepNight]

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

[ข้อมูลจริง] -> [มุมมองของอะแดปเตอร์]

[0: Header]

[0: SleepNight] -> [1: SleepNight]

[1: SleepNight] -> [2: SleepNight]

[2: SleepNight] -> [3: SleepNight.

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

[ข้อมูลจริง] -> [มุมมองของอะแดปเตอร์]

[0: Header] -> [0: Header]

[1: SleepNight] -> [1: SleepNight]

[2: SleepNight] -> [2: SleepNight]

[3: SleepNight] -> [3: SleepNight]

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

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

ขั้นตอนที่ 1: สร้างคลาส DataItem

หากต้องการแยกประเภทรายการและให้ Adapter จัดการเฉพาะ "รายการ" คุณสามารถสร้างคลาสที่เก็บข้อมูลซึ่งแสดงถึง SleepNight หรือ Header จากนั้นชุดข้อมูลจะเป็นรายการของรายการผู้ถือครองข้อมูล

คุณจะรับแอปเริ่มต้นจาก GitHub หรือใช้แอป SleepTracker ที่สร้างใน Codelab ก่อนหน้าต่อไปก็ได้

  1. ดาวน์โหลดโค้ด RecyclerViewHeaders-Starter จาก GitHub ไดเรกทอรี RecyclerViewHeaders-Starter มีแอป SleepTracker เวอร์ชันเริ่มต้นที่จำเป็นสำหรับ Codelab นี้ นอกจากนี้ คุณยังใช้แอปที่สร้างเสร็จแล้วจากโค้ดแล็บก่อนหน้าต่อได้หากต้องการ
  2. เปิด SleepNightAdapter.kt
  3. ที่ระดับบนสุด ให้กำหนดคลาส sealed ที่ชื่อ DataItem ซึ่งแสดงถึงรายการข้อมูลใต้คลาส SleepNightListener

    คลาส sealed จะกำหนดประเภทปิด ซึ่งหมายความว่าต้องกำหนดคลาสย่อยทั้งหมดของ DataItem ในไฟล์นี้ ด้วยเหตุนี้ คอมไพเลอร์จึงทราบจำนวนคลาสย่อย ส่วนอื่นของโค้ดจะกำหนดDataItemประเภทใหม่ที่อาจทำให้ตัวดัดแปลงใช้งานไม่ได้
sealed class DataItem {

 }
  1. ภายในเนื้อหาของคลาส DataItem ให้กำหนด 2 คลาสที่แสดงถึงรายการข้อมูลประเภทต่างๆ ตัวแรกคือ SleepNightItem ซึ่งเป็น Wrapper รอบ SleepNight จึงรับค่าเดียวที่เรียกว่า sleepNight หากต้องการให้เป็นส่วนหนึ่งของคลาสที่ปิดผนึก ให้ขยาย DataItem
data class SleepNightItem(val sleepNight: SleepNight): DataItem()
  1. คลาสที่ 2 คือ Header ซึ่งแสดงถึงส่วนหัว เนื่องจากส่วนหัวไม่มีข้อมูลจริง คุณจึงประกาศเป็น object ได้ ซึ่งหมายความว่าจะมีอินสแตนซ์ของ Header เพียงอินสแตนซ์เดียวเท่านั้น และขยายเวลาอีกครั้งDataItem
object Header: DataItem()
  1. ภายใน DataItem ที่ระดับชั้นเรียน ให้กำหนดพร็อพเพอร์ตี้ abstract Long ชื่อ id เมื่ออแดปเตอร์ใช้ DiffUtil เพื่อพิจารณาว่าสินค้ามีการเปลี่ยนแปลงหรือไม่และอย่างไร DiffItemCallback จะต้องทราบรหัสของสินค้าแต่ละรายการ คุณจะเห็นข้อผิดพลาดเนื่องจาก SleepNightItem และ Header ต้องลบล้างพร็อพเพอร์ตี้แบบนามธรรม id
abstract val id: Long
  1. ใน SleepNightItem ให้ลบล้าง id เพื่อส่งคืน nightId
override val id = sleepNight.nightId
  1. ใน Header ให้ลบล้าง id เพื่อแสดงผล Long.MIN_VALUE ซึ่งเป็นตัวเลขที่เล็กมากๆ (จริงๆ แล้วคือ -2 ยกกำลัง 63) ดังนั้นจึงไม่ขัดแย้งกับ nightId ที่มีอยู่
override val id = Long.MIN_VALUE
  1. โค้ดที่เสร็จสมบูรณ์แล้วควรมีลักษณะดังนี้ และแอปควรสร้างได้โดยไม่มีข้อผิดพลาด
sealed class DataItem {
    abstract val id: Long
    data class SleepNightItem(val sleepNight: SleepNight): DataItem()      {
        override val id = sleepNight.nightId
    }

    object Header: DataItem() {
        override val id = Long.MIN_VALUE
    }
}

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

  1. สร้างเลย์เอาต์สำหรับส่วนหัวในไฟล์ทรัพยากรเลย์เอาต์ใหม่ที่ชื่อ header.xml ซึ่งแสดง TextView ไม่มีอะไรน่าตื่นเต้นเกี่ยวกับเรื่องนี้ ดังนั้นเราจะให้รหัสแก่คุณ
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:text="Sleep Results"
    android:padding="8dp" />
  1. แยก "Sleep Results" เป็นทรัพยากรสตริงและตั้งชื่อว่า header_text
<string name="header_text">Sleep Results</string>
  1. ใน SleepNightAdapter.kt ภายใน SleepNightAdapter เหนือคลาส ViewHolder ให้สร้างคลาส TextViewHolder ใหม่ คลาสนี้จะขยายเลย์เอาต์ textview.xml และแสดงผลอินสแตนซ์ TextViewHolder เนื่องจากคุณเคยทำสิ่งนี้มาก่อนแล้ว เราจึงมีโค้ดให้คุณ และคุณจะต้องนำเข้า View และ R
    class TextViewHolder(view: View): RecyclerView.ViewHolder(view) {
        companion object {
            fun from(parent: ViewGroup): TextViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val view = layoutInflater.inflate(R.layout.header, parent, false)
                return TextViewHolder(view)
            }
        }
    }

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

จากนั้นคุณต้องอัปเดตประกาศของ SleepNightAdapter แทนที่จะรองรับเฉพาะ ViewHolder ประเภทเดียว ก็ต้องใช้ View Holder ได้ทุกประเภท

กำหนดประเภทของสินค้า

  1. ใน SleepNightAdapter.kt ที่ระดับบนสุด ใต้คำสั่ง import และเหนือ SleepNightAdapter ให้กำหนดค่าคงที่ 2 รายการสำหรับประเภทมุมมอง

    RecyclerView จะต้องแยกประเภทมุมมองของแต่ละรายการ เพื่อให้กำหนดตัวยึดมุมมองได้อย่างถูกต้อง
    private val ITEM_VIEW_TYPE_HEADER = 0
    private val ITEM_VIEW_TYPE_ITEM = 1
  1. ภายใน SleepNightAdapter ให้สร้างฟังก์ชันเพื่อลบล้าง getItemViewType() เพื่อแสดงผลค่าคงที่ของส่วนหัวหรือรายการที่ถูกต้องโดยขึ้นอยู่กับประเภทของรายการปัจจุบัน
override fun getItemViewType(position: Int): Int {
        return when (getItem(position)) {
            is DataItem.Header -> ITEM_VIEW_TYPE_HEADER
            is DataItem.SleepNightItem -> ITEM_VIEW_TYPE_ITEM
        }
    }

อัปเดตคำจำกัดความของ SleepNightAdapter

  1. ในคำจำกัดความของ SleepNightAdapter ให้อัปเดตอาร์กิวเมนต์แรกสำหรับ ListAdapter จาก SleepNight เป็น DataItem
  2. ในคำจำกัดความของ SleepNightAdapter ให้เปลี่ยนอาร์กิวเมนต์ทั่วไปที่ 2 สำหรับ ListAdapter จาก SleepNightAdapter.ViewHolder เป็น RecyclerView.ViewHolder คุณจะเห็นข้อผิดพลาดบางอย่างสำหรับการอัปเดตที่จำเป็น และส่วนหัวของชั้นเรียนควรมีลักษณะดังที่แสดงด้านล่าง
class SleepNightAdapter(val clickListener: SleepNightListener):
       ListAdapter<DataItem, RecyclerView.ViewHolder>(SleepNightDiffCallback()) {

อัปเดต onCreateViewHolder()

  1. เปลี่ยนลายเซ็นของ onCreateViewHolder() เพื่อส่งคืน RecyclerView.ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
  1. ขยายการใช้งานเมธอด onCreateViewHolder() เพื่อทดสอบและแสดงผลตัวยึดมุมมองที่เหมาะสมสำหรับสินค้าแต่ละประเภท วิธีการที่อัปเดตแล้วควรมีลักษณะเหมือนโค้ดด้านล่าง
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            ITEM_VIEW_TYPE_HEADER -> TextViewHolder.from(parent)
            ITEM_VIEW_TYPE_ITEM -> ViewHolder.from(parent)
            else -> throw ClassCastException("Unknown viewType ${viewType}")
        }
    }

Update onBindViewHolder()

  1. เปลี่ยนประเภทพารามิเตอร์ของ onBindViewHolder() จาก ViewHolder เป็น RecyclerView.ViewHolder
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
  1. เพิ่มเงื่อนไขเพื่อกำหนดข้อมูลให้กับผู้ถือข้อมูลพร็อพเพอร์ตี้เท่านั้น หากผู้ถือข้อมูลเป็น ViewHolder
        when (holder) {
            is ViewHolder -> {...}
  1. แคสต์ประเภทออบเจ็กต์ที่ getItem() แสดงผลเป็น DataItem.SleepNightItem ฟังก์ชัน onBindViewHolder() ที่เสร็จสมบูรณ์แล้วควรมีลักษณะดังนี้
  override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is ViewHolder -> {
                val nightItem = getItem(position) as DataItem.SleepNightItem
                holder.bind(nightItem.sleepNight, clickListener)
            }
        }
    }

อัปเดตการเรียกกลับ DiffUtil

  1. เปลี่ยนเมธอดใน SleepNightDiffCallback เพื่อใช้คลาส DataItem ใหม่แทน SleepNight ระงับคำเตือน Lint ดังที่แสดงในโค้ดด้านล่าง
class SleepNightDiffCallback : DiffUtil.ItemCallback<DataItem>() {
    override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
        return oldItem.id == newItem.id
    }
    @SuppressLint("DiffUtilEquals")
    override fun areContentsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
        return oldItem == newItem
    }
}

เพิ่มและส่งส่วนหัว

  1. ภายใน SleepNightAdapter ใต้ onCreateViewHolder() ให้กำหนดฟังก์ชัน addHeaderAndSubmitList() ดังที่แสดงด้านล่าง ฟังก์ชันนี้รับรายการของ SleepNight คุณจะใช้ฟังก์ชันนี้เพื่อเพิ่มส่วนหัวแล้วส่งรายการแทนการใช้ submitList() ที่ListAdapter มีให้เพื่อส่งรายการ
fun addHeaderAndSubmitList(list: List<SleepNight>?) {}
  1. ภายใน addHeaderAndSubmitList() หากรายการที่ส่งเข้ามาคือ null ให้ส่งคืนเฉพาะส่วนหัว ไม่เช่นนั้น ให้แนบส่วนหัวไว้ที่ส่วนหัวของรายการ แล้วส่งรายการ
val items = when (list) {
                null -> listOf(DataItem.Header)
                else -> listOf(DataItem.Header) + list.map { DataItem.SleepNightItem(it) }
            }
submitList(items)
  1. เปิด SleepTrackerFragment.kt แล้วเปลี่ยนการเรียกใช้ submitList() เป็น addHeaderAndSubmitList()
  1. เรียกใช้แอปและสังเกตว่าส่วนหัวแสดงเป็นรายการแรกในรายการรายการการนอนหลับอย่างไร

แอปนี้ต้องได้รับการแก้ไข 2 อย่าง โดยอย่างหนึ่งมองเห็นได้และอีกอย่างหนึ่งมองไม่เห็น

  • ส่วนหัวจะปรากฏที่มุมซ้ายบนและสังเกตได้ยาก
  • ซึ่งอาจไม่สำคัญมากสำหรับรายการสั้นๆ ที่มีส่วนหัวเดียว แต่คุณไม่ควรทำการจัดการรายการใน addHeaderAndSubmitList() ในเทรด UI ลองนึกถึงรายการที่มีหลายร้อยรายการ ส่วนหัวหลายรายการ และตรรกะที่ใช้ตัดสินใจว่าจะแทรกรายการไว้ที่ใด งานนี้ควรอยู่ในโครูทีน

เปลี่ยน addHeaderAndSubmitList() เพื่อใช้โครูทีน

  1. ที่ระดับบนสุดภายในSleepNightAdapter คลาส ให้กำหนดCoroutineScopeด้วย Dispatchers.Default
private val adapterScope = CoroutineScope(Dispatchers.Default)
  1. ใน addHeaderAndSubmitList() ให้เปิดใช้โครูทีนใน adapterScope เพื่อจัดการรายการ จากนั้นเปลี่ยนไปใช้บริบท Dispatchers.Main เพื่อส่งรายการ ดังที่แสดงในโค้ดด้านล่าง
 fun addHeaderAndSubmitList(list: List<SleepNight>?) {
        adapterScope.launch {
            val items = when (list) {
                null -> listOf(DataItem.Header)
                else -> listOf(DataItem.Header) + list.map { DataItem.SleepNightItem(it) }
            }
            withContext(Dispatchers.Main) {
                submitList(items)
            }
        }
    }
  1. โค้ดควรจะสร้างและเรียกใช้ได้ และคุณจะไม่เห็นความแตกต่างใดๆ

ปัจจุบันส่วนหัวมีความกว้างเท่ากับรายการอื่นๆ ในตารางกริด โดยใช้ 1 ช่วงในแนวนอนและแนวตั้ง ทั้งกริดพอดีกับรายการ 3 รายการที่มีความกว้าง 1 ช่วงในแนวนอน ดังนั้นส่วนหัวควรใช้ 3 ช่วงในแนวนอน

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

  1. เปิด SleepTrackerFragment.kt
  2. ค้นหารหัสที่คุณกำหนด manager ที่ส่วนท้ายของ onCreateView()
val manager = GridLayoutManager(activity, 3)
  1. ด้านล่าง manager ให้กำหนด manager.spanSizeLookup ดังที่แสดง คุณต้องสร้าง object เนื่องจาก setSpanSizeLookup ไม่รับ lambda หากต้องการสร้าง object ใน Kotlin ให้พิมพ์ object : classname ในกรณีนี้คือ GridLayoutManager.SpanSizeLookup
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
}
  1. คุณอาจได้รับข้อผิดพลาดของคอมไพเลอร์ในการเรียกใช้ตัวสร้าง หากต้องการ ให้เปิดเมนูความตั้งใจด้วย Option+Enter (Mac) หรือ Alt+Enter (Windows) เพื่อใช้การเรียกตัวสร้าง
  1. จากนั้นคุณจะได้รับข้อผิดพลาดใน object ที่ระบุว่าคุณต้องลบล้างเมธอด วางเคอร์เซอร์บน object กด Option+Enter (Mac) หรือ Alt+Enter (Windows) เพื่อเปิดเมนูความตั้งใจ แล้วลบล้างเมธอด getSpanSize()
  1. ในเนื้อหาของ getSpanSize() ให้ส่งขนาดช่วงที่ถูกต้องสำหรับแต่ละตำแหน่ง ตำแหน่ง 0 มีขนาดช่วงเป็น 3 และตำแหน่งอื่นๆ มีขนาดช่วงเป็น 1 โค้ดที่เสร็จสมบูรณ์แล้วควรมีลักษณะเหมือนโค้ดด้านล่าง
    manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
            override fun getSpanSize(position: Int) =  when (position) {
                0 -> 3
                else -> 1
            }
        }
  1. หากต้องการปรับปรุงลักษณะส่วนหัว ให้เปิด header.xml แล้วเพิ่มโค้ดนี้ลงในไฟล์เลย์เอาต์ header.xml
android:textColor="@color/white_text_color"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="@color/colorAccent"
  1. เรียกใช้แอปของคุณ ซึ่งควรมีลักษณะคล้ายกับภาพหน้าจอด้านล่าง

ยินดีด้วย คุณดำเนินการเสร็จแล้ว

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

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

ขั้นตอนสำคัญในการเพิ่มส่วนหัวมีดังนี้

  • ดึงข้อมูลในรายการโดยสร้าง DataItem ที่สามารถเก็บส่วนหัวหรือข้อมูลได้
  • สร้างตัวยึดมุมมองที่มีเลย์เอาต์สำหรับส่วนหัวในอะแดปเตอร์
  • อัปเดตอแดปเตอร์และเมธอดของอแดปเตอร์เพื่อใช้ RecyclerView.ViewHolder ทุกประเภท
  • ใน onCreateViewHolder() ให้แสดงผลประเภทที่ถูกต้องของตัวยึดมุมมองสำหรับรายการข้อมูล
  • อัปเดต SleepNightDiffCallback เพื่อให้ใช้งานร่วมกับชั้นเรียน DataItem ได้
  • สร้างaddHeaderAndSubmitList()ฟังก์ชันที่ใช้โครูทีนเพื่อเพิ่มส่วนหัวลงในชุดข้อมูล แล้วเรียกใช้ submitList()
  • ใช้ GridLayoutManager.SpanSizeLookup() เพื่อให้ส่วนหัวมีความกว้าง 3 ช่วงเท่านั้น

หลักสูตร Udacity:

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

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

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

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

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

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

คำถามที่ 1

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

▢ อะแดปเตอร์สามารถใช้ViewHolderคลาสหลายคลาสเพื่อเก็บส่วนหัวและข้อมูลประเภทต่างๆ

▢ คุณมีตัวยึดมุมมองสำหรับข้อมูลและตัวยึดมุมมองสำหรับส่วนหัวได้อย่างละ 1 รายการ

RecyclerView รองรับส่วนหัวหลายประเภท แต่ข้อมูลต้องเป็นรูปแบบเดียวกัน

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

คำถามที่ 2

คุณควรใช้โครูทีนกับ RecyclerView เมื่อใด เลือกข้อความทั้งหมดที่เป็นจริง

▢ ไม่เลย RecyclerView เป็นองค์ประกอบ UI และไม่ควรใช้โครูทีน

▢ ใช้โครูทีนสำหรับงานที่ใช้เวลานานซึ่งอาจทำให้ UI ทำงานช้าลง

▢ การจัดการรายการอาจใช้เวลานาน และคุณควรใช้โครูทีนเสมอ

▢ ใช้โครูทีนกับฟังก์ชันระงับเพื่อหลีกเลี่ยงการบล็อกเทรดหลัก

คำถามที่ 3

คุณไม่จำเป็นต้องทำสิ่งใดต่อไปนี้เมื่อใช้ ViewHolder มากกว่า 1 รายการ

▢ ใน ViewHolder ให้ระบุไฟล์เลย์เอาต์หลายไฟล์เพื่อขยายตามต้องการ

▢ ใน onCreateViewHolder() ให้แสดงผลประเภทที่ถูกต้องของตัวยึดมุมมองสำหรับรายการข้อมูล

▢ ใน onBindViewHolder() ให้ผูกข้อมูลเฉพาะในกรณีที่ตัวยึดมุมมองเป็นตัวยึดมุมมองประเภทที่ถูกต้องสำหรับรายการข้อมูล

▢ ทำให้ลายเซ็นของคลาสอะแดปเตอร์เป็นแบบทั่วไปเพื่อยอมรับ RecyclerView.ViewHolder

เริ่มบทเรียนถัดไป: 8.1 การรับข้อมูลจากอินเทอร์เน็ต

ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin