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
ที่ระดับชั้นเรียน ให้กำหนดพร็อพเพอร์ตี้abstract
Long
ชื่อ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