前一天介紹的 QueryResultViewModel.kt 說明了如何將查詢頁的邏輯和狀態串接起來,不過在這當中,我們還沒處理列表的顯示。
今天要讀的 EmptyRoomAdapter.kt 是把空教室資料顯示到 RecyclerView(列表) 的 Adapter(適配器)。
這裡要注意的是,Adapter 是在 QueryResultActivity.kt 裡建立的,然後接收 QueryResultViewModel.kt 裡已經篩選好的空教室 (emptyRooms),再顯示到 RecyclerView。這部分看似有點麻煩,但實則上很有 MVVM 的職責分離。
一開始我只知道列表的顯示可能是需要用到 RecyclerView ,但不太清楚細節要怎麼完成裡面的操作。後來才知道,原來我們需要一個專門處理這項工作的 Adapter ,把資料先做分組與設計,再交給 RecyclerView 顯示。
簡單來說,兩者的分工就像是:
RecyclerView(舞台 / 展示架): 它只負責提供一個可以高效滾動的容器與外殼,它不知道資料長什麼樣子,也不管資料怎麼拿。Adapter(展覽佈置師 / 資料轉換器): 它扮演資料與畫面之間的橋樑。負責把 ViewModel 送來的 emptyRooms 資料,一筆筆「搬進」UI 項目裡,並告訴 RecyclerView 哪裡該放「樓層標題」、哪裡該放「教室卡片」。
EmptyRoomAdapter.kt 負責什麼和 Hermes Agent 一起重讀完檔案後,我了解到EmptyRoomAdapter.kt 的核心任務,就是負責把 ViewModel 計算好的空教室清單(rooms),整理成「樓層標題 + 教室項目」的混合清單,再顯示到 RecyclerView 上。
其中,裡面有兩個最關鍵的設計:
1. 綁定資料與點擊事件
// 根據型別判斷並綁定畫面與事件
holder.headerTitle.text = items[position] as String
val room = items[position] as ClassroomSchedule
// 設定「教室名稱」
holder.roomNameTextView.text = room.classroom
// 設定 itemView,設定點擊監聽器
holder.itemView.setOnClickListener {
onItemClick(room)
}
onBindViewHolder() 負責把資料綁定到畫面上。String 顯示;如果是教室項目,除了填入教室名稱,還會在 itemView 設定點擊監聽器,把點擊的教室資料透過 Callback 傳回 Activity 。2. 更新資料並加上樓層標題
fun updateRoomsAndAddHeaders(newRooms: List<ClassroomSchedule>) {
val newItems = mutableListOf<Any>()
// 透過 extractFloor 提取教室名稱中的數字並進行 GroupBy
val groupedByFloor = newRooms.groupBy { extractFloor(it.classroom) }
val sortedFloors = groupedByFloor.keys.sorted()
for (floor in sortedFloors) {
newItems.add("-- ${floor}樓 --") // 插入標題字串
groupedByFloor[floor]?.sortedBy { it.classroom }?.let { newItems.addAll(it) }
}
items = newItems
notifyDataSetChanged()
}
groupBy) → 樓層排序 → 動態插入標題字串(如 -- 1樓 --) → 插入該樓層教室 ➔ 更新清單並發送通知。讓使用者看到的畫面不再是密密麻麻的一串名單,而是層次分明的分組列表目前 EmptyRoomAdapter.kt 的處理流程大概如下:
emptyRooms
↓
EmptyRoomAdapter.kt
↓
依樓層分組並加入樓層標題
↓
根據項目類型選擇對應 Layout
↓
RecyclerView 顯示空教室
↓
點擊教室
↓
回傳選取結果給 Activity
EmptyRoomAdapter 是空教室資料顯示到 RecyclerView 的 Adapter:
QueryResultViewModel 已經篩選好的 emptyRooms,並在 updateRoomsAndAddHeaders() 中依照教室名稱提取樓層、分組、排序,最後整理成包含樓層標題與教室項目的 items。String 樓層標題與 ClassroomSchedule 教室資料,所以 items 使用 List<Any>。getItemViewType() 判斷每一列的型別,再建立對應的 ViewHolder。QueryResultActivity。讀完 EmptyRoomAdapter 後,我理解到 RecyclerView 不會自己知道資料要怎麼顯示。
ViewModel 算出的是 List<ClassroomSchedule>,但畫面需要的是一列一列的 UI,而且 RoomRush 還希望依照樓層分段顯示。因此 Adapter 先把空教室資料依樓層分組,再插入像 -- 1樓 -- 這樣的樓層標題,最後才交給 RecyclerView 顯示。
這個檔案也讓我理解 Adapter 的職責分離:它不查資料庫,也不判斷哪些教室是空的,它只負責「資料如何變成畫面」。真正的查詢邏輯在 ViewModel,畫面事件的下一步則交回 Activity 處理。
用一句話來說明這檔案:
EmptyRoomAdapter不查詢空教室,只把emptyRooms整理成有樓層標題的 RecyclerView 列表。
讀完 EmptyRoomAdapter.kt 如何把空教室資料呈現成漂亮的分組列表後,我們知道裡面還隱藏了關鍵的點擊監聽器(onItemClick),使用者點擊列表中的某一間教室後,詳細頁如何知道要顯示哪一間教室?
這部分會在 RoomDetailActivity,kt 以及 RoomDetailViewModel.kt 中來解密——看 App 如何把一間教室的完整動態送到使用者眼前!