Android Kotlin Fundamentals 07.5: ส่วนหัวใน RecyclerView

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[1: SleepNight] -> [1: การตรวจจับการนอนหลับ]

[2: SleepNight] -> [2: การตรวจจับการนอนหลับ]

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

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

[0: ส่วนหัว]

[0: SleepNight] -> [1: การตรวจจับการนอนหลับ]

[1: SleepNight] -> [2: การตรวจจับการนอนหลับ]

[2: SleepNight] -> [3: SleepNight

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

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

[0: ส่วนหัว] -> [0: ส่วนหัว]

[1: SleepNight] -> [1: การตรวจจับการนอนหลับ]

[2: SleepNight] -> [2: การตรวจจับการนอนหลับ]

[3: SleepNight] -> [3: การตรวจจับการนอนหลับ]

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

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

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

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

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

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

    คลาส 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 ที่ระดับพร็อพเพอร์ตี้ ให้กําหนดพร็อพเพอร์ตี้ Long ของ abstract ชื่อ 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 เพียงประเภทเดียว

กําหนดประเภทของรายการ

  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}")
        }
    }

อัปเดต 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 อย่างที่ต้องแก้ไขสําหรับแอปนี้ คือแบบหนึ่งและบางแบบไม่เห็น

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

เปลี่ยน addHeaderAndSubmitList() เพื่อใช้ Coroutine:

  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 ไม่ถือเป็นแลมบ์ดา หากต้องการใช้ object ใน Kotlin ให้พิมพ์ object : classname ในกรณีนี้คือ GridLayoutManager.SpanSizeLookup
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
}
  1. คุณอาจได้รับข้อผิดพลาดคอมไพเลอร์เพื่อเรียกใช้ตัวสร้าง หากเป็นเช่นนั้น ให้เปิดเมนู Intent ที่มี Option+Enter (Mac) หรือ Alt+Enter (Windows) เพื่อใช้การเรียกเครื่องมือสร้าง
  1. จากนั้นพบข้อผิดพลาดใน object ซึ่งแจ้งว่าคุณต้องลบล้างเมธอด วางเคอร์เซอร์ที่ object กด Option+Enter (Mac) หรือ Alt+Enter (Windows) เพื่อเปิดเมนู Intent แล้วลบล้างเมธอด 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() ที่ใช้ Coroutine เพื่อเพิ่มส่วนหัวในชุดข้อมูล จากนั้นเรียกใช้ submitList()
  • นํา GridLayoutManager.SpanSizeLookup() ไปใช้เพื่อให้เฉพาะส่วนหัว 3 ช่องครอบคลุมความกว้างเท่านั้น

หลักสูตร Udacity:

เอกสารประกอบสําหรับนักพัฒนาซอฟต์แวร์ Android

ส่วนนี้จะอธิบายการบ้านและรายงานสําหรับนักเรียนที่ทํางานผ่าน Codelab นี้ซึ่งเป็นส่วนหนึ่งของหลักสูตรที่นําโดยผู้สอน สิ่งที่ผู้สอนต้องทํามีดังนี้

  • มอบหมายการบ้านหากจําเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานทําการบ้าน
  • ตัดเกรดการบ้าน

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

หากคุณใช้ Codelab ด้วยตัวเอง ก็ให้ใช้การบ้านเพื่อทดสอบความรู้ของคุณได้

ตอบคําถามเหล่านี้

คำถามที่ 1

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

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

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

▢ A RecyclerView รองรับส่วนหัวหลายประเภท แต่ข้อมูลต้องเหมือนกันทุกประการ

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

คำถามที่ 2

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

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

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

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

▢ โดยใช้ Coroutine กับฟังก์ชันการระงับเพื่อหลีกเลี่ยงการบล็อกชุดข้อความหลัก

คำถามที่ 3

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

▢ ใน ViewHolder ให้ส่งไฟล์เลย์เอาต์หลายไฟล์ให้สูงเกินจําเป็น

▢ ใน onCreateViewHolder() แสดงผลประเภทเจ้าของข้อมูลพร็อพเพอร์ตี้ที่ถูกต้องสําหรับรายการข้อมูล

▢ ใน onBindViewHolder() ให้เชื่อมโยงข้อมูลเฉพาะในกรณีที่ผู้ถือข้อมูลพร็อพเพอร์ตี้เป็นประเภทข้อมูลพร็อพเพอร์ตี้ที่ถูกต้องสําหรับรายการข้อมูล

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

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

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