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 ก่อนหน้าต่อไปก็ได้
- ดาวน์โหลดโค้ด RecyclerViewHeaders-Starter จาก GitHub ไดเรกทอรี RecyclerViewHeaders-Starter มีแอป SleepTracker เวอร์ชันเริ่มต้นที่จำเป็นสำหรับ Codelab นี้ นอกจากนี้ คุณยังใช้แอปที่สร้างเสร็จแล้วจากโค้ดแล็บก่อนหน้าต่อได้หากต้องการ
- เปิด SleepNightAdapter.kt
- ที่ระดับบนสุด ให้กำหนดคลาส
sealedที่ชื่อDataItemซึ่งแสดงถึงรายการข้อมูลใต้คลาสSleepNightListener
คลาสsealedจะกำหนดประเภทปิด ซึ่งหมายความว่าต้องกำหนดคลาสย่อยทั้งหมดของDataItemในไฟล์นี้ ด้วยเหตุนี้ คอมไพเลอร์จึงทราบจำนวนคลาสย่อย ส่วนอื่นของโค้ดจะกำหนดDataItemประเภทใหม่ที่อาจทำให้ตัวดัดแปลงใช้งานไม่ได้
sealed class DataItem {
}- ภายในเนื้อหาของคลาส
DataItemให้กำหนด 2 คลาสที่แสดงถึงรายการข้อมูลประเภทต่างๆ ตัวแรกคือSleepNightItemซึ่งเป็น Wrapper รอบSleepNightจึงรับค่าเดียวที่เรียกว่าsleepNightหากต้องการให้เป็นส่วนหนึ่งของคลาสที่ปิดผนึก ให้ขยายDataItem
data class SleepNightItem(val sleepNight: SleepNight): DataItem()- คลาสที่ 2 คือ
Headerซึ่งแสดงถึงส่วนหัว เนื่องจากส่วนหัวไม่มีข้อมูลจริง คุณจึงประกาศเป็นobjectได้ ซึ่งหมายความว่าจะมีอินสแตนซ์ของHeaderเพียงอินสแตนซ์เดียวเท่านั้น และขยายเวลาอีกครั้งDataItem
object Header: DataItem()- ภายใน
DataItemที่ระดับชั้นเรียน ให้กำหนดพร็อพเพอร์ตี้abstractLongชื่อidเมื่ออแดปเตอร์ใช้DiffUtilเพื่อพิจารณาว่าสินค้ามีการเปลี่ยนแปลงหรือไม่และอย่างไรDiffItemCallbackจะต้องทราบรหัสของสินค้าแต่ละรายการ คุณจะเห็นข้อผิดพลาดเนื่องจากSleepNightItemและHeaderต้องลบล้างพร็อพเพอร์ตี้แบบนามธรรมid
abstract val id: Long- ใน
SleepNightItemให้ลบล้างidเพื่อส่งคืนnightId
override val id = sleepNight.nightId- ใน
Headerให้ลบล้างidเพื่อแสดงผลLong.MIN_VALUEซึ่งเป็นตัวเลขที่เล็กมากๆ (จริงๆ แล้วคือ -2 ยกกำลัง 63) ดังนั้นจึงไม่ขัดแย้งกับnightIdที่มีอยู่
override val id = Long.MIN_VALUE- โค้ดที่เสร็จสมบูรณ์แล้วควรมีลักษณะดังนี้ และแอปควรสร้างได้โดยไม่มีข้อผิดพลาด
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 สำหรับส่วนหัว
- สร้างเลย์เอาต์สำหรับส่วนหัวในไฟล์ทรัพยากรเลย์เอาต์ใหม่ที่ชื่อ 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" />- แยก
"Sleep Results"เป็นทรัพยากรสตริงและตั้งชื่อว่าheader_text
<string name="header_text">Sleep Results</string>- ใน 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 ได้ทุกประเภท
กำหนดประเภทของสินค้า
- ใน
SleepNightAdapter.ktที่ระดับบนสุด ใต้คำสั่งimportและเหนือSleepNightAdapterให้กำหนดค่าคงที่ 2 รายการสำหรับประเภทมุมมองRecyclerViewจะต้องแยกประเภทมุมมองของแต่ละรายการ เพื่อให้กำหนดตัวยึดมุมมองได้อย่างถูกต้อง
private val ITEM_VIEW_TYPE_HEADER = 0
private val ITEM_VIEW_TYPE_ITEM = 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
- ในคำจำกัดความของ
SleepNightAdapterให้อัปเดตอาร์กิวเมนต์แรกสำหรับListAdapterจากSleepNightเป็นDataItem - ในคำจำกัดความของ
SleepNightAdapterให้เปลี่ยนอาร์กิวเมนต์ทั่วไปที่ 2 สำหรับListAdapterจากSleepNightAdapter.ViewHolderเป็นRecyclerView.ViewHolderคุณจะเห็นข้อผิดพลาดบางอย่างสำหรับการอัปเดตที่จำเป็น และส่วนหัวของชั้นเรียนควรมีลักษณะดังที่แสดงด้านล่าง
class SleepNightAdapter(val clickListener: SleepNightListener):
ListAdapter<DataItem, RecyclerView.ViewHolder>(SleepNightDiffCallback()) {อัปเดต onCreateViewHolder()
- เปลี่ยนลายเซ็นของ
onCreateViewHolder()เพื่อส่งคืนRecyclerView.ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder- ขยายการใช้งานเมธอด
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()
- เปลี่ยนประเภทพารามิเตอร์ของ
onBindViewHolder()จากViewHolderเป็นRecyclerView.ViewHolder
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)- เพิ่มเงื่อนไขเพื่อกำหนดข้อมูลให้กับผู้ถือข้อมูลพร็อพเพอร์ตี้เท่านั้น หากผู้ถือข้อมูลเป็น
ViewHolder
when (holder) {
is ViewHolder -> {...}- แคสต์ประเภทออบเจ็กต์ที่
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
- เปลี่ยนเมธอดใน
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
}
}เพิ่มและส่งส่วนหัว
- ภายใน
SleepNightAdapterใต้onCreateViewHolder()ให้กำหนดฟังก์ชันaddHeaderAndSubmitList()ดังที่แสดงด้านล่าง ฟังก์ชันนี้รับรายการของSleepNightคุณจะใช้ฟังก์ชันนี้เพื่อเพิ่มส่วนหัวแล้วส่งรายการแทนการใช้submitList()ที่ListAdapterมีให้เพื่อส่งรายการ
fun addHeaderAndSubmitList(list: List<SleepNight>?) {}- ภายใน
addHeaderAndSubmitList()หากรายการที่ส่งเข้ามาคือnullให้ส่งคืนเฉพาะส่วนหัว ไม่เช่นนั้น ให้แนบส่วนหัวไว้ที่ส่วนหัวของรายการ แล้วส่งรายการ
val items = when (list) {
null -> listOf(DataItem.Header)
else -> listOf(DataItem.Header) + list.map { DataItem.SleepNightItem(it) }
}
submitList(items)- เปิด SleepTrackerFragment.kt แล้วเปลี่ยนการเรียกใช้
submitList()เป็นaddHeaderAndSubmitList()
- เรียกใช้แอปและสังเกตว่าส่วนหัวแสดงเป็นรายการแรกในรายการรายการการนอนหลับอย่างไร

แอปนี้ต้องได้รับการแก้ไข 2 อย่าง โดยอย่างหนึ่งมองเห็นได้และอีกอย่างหนึ่งมองไม่เห็น
- ส่วนหัวจะปรากฏที่มุมซ้ายบนและสังเกตได้ยาก
- ซึ่งอาจไม่สำคัญมากสำหรับรายการสั้นๆ ที่มีส่วนหัวเดียว แต่คุณไม่ควรทำการจัดการรายการใน
addHeaderAndSubmitList()ในเทรด UI ลองนึกถึงรายการที่มีหลายร้อยรายการ ส่วนหัวหลายรายการ และตรรกะที่ใช้ตัดสินใจว่าจะแทรกรายการไว้ที่ใด งานนี้ควรอยู่ในโครูทีน
เปลี่ยน addHeaderAndSubmitList() เพื่อใช้โครูทีน
- ที่ระดับบนสุดภายใน
SleepNightAdapterคลาส ให้กำหนดCoroutineScopeด้วยDispatchers.Default
private val adapterScope = CoroutineScope(Dispatchers.Default)- ใน
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 ช่วงในแนวนอนและแนวตั้ง ทั้งกริดพอดีกับรายการ 3 รายการที่มีความกว้าง 1 ช่วงในแนวนอน ดังนั้นส่วนหัวควรใช้ 3 ช่วงในแนวนอน
หากต้องการแก้ไขความกว้างของส่วนหัว คุณต้องบอก GridLayoutManager ว่าเมื่อใดควรขยายข้อมูลในทุกคอลัมน์ ซึ่งทำได้โดยการกำหนดค่า SpanSizeLookup ใน GridLayoutManager ซึ่งเป็นออบเจ็กต์การกำหนดค่าที่ GridLayoutManager ใช้เพื่อกำหนดจำนวนช่วงที่จะใช้สำหรับแต่ละรายการในลิสต์
- เปิด SleepTrackerFragment.kt
- ค้นหารหัสที่คุณกำหนด
managerที่ส่วนท้ายของonCreateView()
val manager = GridLayoutManager(activity, 3)- ด้านล่าง
managerให้กำหนดmanager.spanSizeLookupดังที่แสดง คุณต้องสร้างobjectเนื่องจากsetSpanSizeLookupไม่รับ lambda หากต้องการสร้างobjectใน Kotlin ให้พิมพ์object : classnameในกรณีนี้คือGridLayoutManager.SpanSizeLookup
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
}- คุณอาจได้รับข้อผิดพลาดของคอมไพเลอร์ในการเรียกใช้ตัวสร้าง หากต้องการ ให้เปิดเมนูความตั้งใจด้วย
Option+Enter(Mac) หรือAlt+Enter(Windows) เพื่อใช้การเรียกตัวสร้าง
- จากนั้นคุณจะได้รับข้อผิดพลาดใน
objectที่ระบุว่าคุณต้องลบล้างเมธอด วางเคอร์เซอร์บนobjectกดOption+Enter(Mac) หรือAlt+Enter(Windows) เพื่อเปิดเมนูความตั้งใจ แล้วลบล้างเมธอดgetSpanSize()
- ในเนื้อหาของ
getSpanSize()ให้ส่งขนาดช่วงที่ถูกต้องสำหรับแต่ละตำแหน่ง ตำแหน่ง 0 มีขนาดช่วงเป็น 3 และตำแหน่งอื่นๆ มีขนาดช่วงเป็น 1 โค้ดที่เสร็จสมบูรณ์แล้วควรมีลักษณะเหมือนโค้ดด้านล่าง
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int) = when (position) {
0 -> 3
else -> 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"- เรียกใช้แอปของคุณ ซึ่งควรมีลักษณะคล้ายกับภาพหน้าจอด้านล่าง

ยินดีด้วย คุณดำเนินการเสร็จแล้ว
โปรเจ็กต์ 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
เริ่มบทเรียนถัดไป:
ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin


