iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
Software Development

從 Vibe Coding 到可維護專案:用 Hermes Agent 重讀 RoomRush Android 專題系列 第 24 篇

Day 24 | 一張教室卡片背後,藏著完整課表的入口

  • 分享至 

  • xImage
  •  

前言:教室卡片如何顯示,並跳到完整課表頁?

上一篇我們拆解了「所有教室狀態」頁面的整體架構與資料流,今天我們要更進一步,深入探討這個頁面的視覺與互動核心。

在總覽畫面上,每一間教室都被封裝成一張專屬的資訊卡片,讓管理者能一眼看清教室內容,並能直接點擊卡片跳轉至該教室的「完整每週課表」。而這背後負責將資料轉化為卡片視圖、並綁定跳頁事件的幕後功臣,正是 AllClassroomsAdapter.kt。今天就讓我們一起來深究這檔案的實作細節。


一開始我認為

這頁面上的 Adapter 設計其實和前面「教室細項設定」的 ClassroomAdapter.kt 相似,只是將原本的單一文字列換成了帶有「教室類型」與「是否可飲食」的卡片佈局。

但這正好體現了「純選單」走向「卡片化資訊看板」的轉變:我們不再只傳入純教室名稱,而是將資料升級為具備完整屬性的 ClassroomInfo UI Model。

這讓每一張教室卡片不再只是一個跳頁按鈕,而是具備即時資訊呈現的獨立單元,真正達成了「一眼綜觀所有教室資訊」的設計初衷。


實際讀完後,AllClassroomsAdapter.kt 負責什麼

和 Hermes Agent 一起重讀 AllClassroomsAdapter.kt 後,它負責是「所有教室狀態總覽頁」的 RecyclerView Adapter。它接收了 List<ClassroomInfo>,把每間教室顯示成一張卡片,卡片上顯示教室名稱、類型、是否可飲食;當管理者點擊某張卡片時,會進入到下一頁,並用 CLASSROOM_NAME 傳出教室名稱。

整個檔案主要的任務可以分為以下三點:

1. 專屬 UI Model 與卡片視圖資料綁定

// 1. 專為卡片 UI 設計的輕量資料類
data class ClassroomInfo(
    val name: String,
    val type: String,
    val foodAllowed: Boolean
)

// 2. ViewHolder 欄位映射
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val nameTextView: TextView = itemView.findViewById(R.id.classroom_name_text)
    private val typeTextView: TextView = itemView.findViewById(R.id.classroom_type_text)
    private val foodAllowedTextView: TextView = itemView.findViewById(R.id.food_allowed_text)
// 英文代碼轉為中文
fun bind(classroom: ClassroomInfo) {
    nameTextView.text = classroom.name
    typeTextView.text = when (classroom.type) {
        "Normal" -> "一般教室"
        "Computer" -> "電腦教室"
        "Drawing" -> "製圖教室"
        "Lab" -> "實驗室"
        "MeetingRoom" -> "研討室"
        else -> "未知"
    }
    //是否可飲食用 true → 是、false → 否 顯示
    foodAllowedTextView.text = if (classroom.foodAllowed) "是" else "否"
}

  • 架構優勢:
    • ClassroomInfo 將畫面需要的屬性精準收保留至 3 個欄位,切斷 View 與 Database Entity 的直接依賴。
    • 將代碼轉譯邏輯集中在 bind(),讓 CardView 上的 item_all_classrooms.xml 能一目了然呈現「教室名稱」、「教室類型」與「是否可飲食」。

2. 綁定資料與點擊跳頁

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val classroom = classrooms[position]
    holder.bind(classroom)
    // 點擊卡片跳轉至該教室的完整每週課表
    holder.itemView.setOnClickListener {
        val context = holder.itemView.context
        val intent = Intent(context, ClassroomScheduleDetailActivity::class.java).apply {
            putExtra("CLASSROOM_NAME", classroom.name)
        }
        context.startActivity(intent)
    }
  • 職責說明: 當管理者在總覽清單中點擊特定卡片時,Adapter 提取當前項目的教室名稱(如 SF304),透過 Intent Extra 將 CLASSROOM_NAME 帶往 ClassroomScheduleDetailActivity,完成「總覽 ➔ 單一教室完整課表」的跳轉。

3. 更新資料

@SuppressLint("NotifyDataSetChanged")
fun updateData(newClassrooms: List<ClassroomInfo>) {
    this.classrooms = newClassrooms
    notifyDataSetChanged()
}
  • 技術細節:當 AllClassroomsActivity observe 到新的教室資料時,會呼叫 updateData();這裡用 notifyDataSetChanged() 讓整份列表重新刷新。

它接在哪一條流程上

這條流程主要呈現從教室總覽進入完整課表的過程:

  1. AllClassroomsActivity 將教室資料整理成 List<ClassroomInfo>。
  2. AllClassroomsAdapter 接收資料,並透過 RecyclerView 將每間教室顯示成卡片。
  3. 使用者點擊卡片後,系統取得該教室的 CLASSROOM_NAME。
  4. 透過 Intent 將教室名稱傳遞至 AllClassroomsSchedule.kt,再顯示該教室的完整課表。
AllClassroomsActivity
「所有教室狀態總覽」
        │
        ▼
List<ClassroomInfo>
「整理教室總覽資料」
        │
        ▼
AllClassroomsAdapter
「接收並顯示教室資料」
        │
        ▼
RecyclerView
「每間教室顯示成一張卡片」
        │
        ▼
使用者點擊教室卡片
        │
        ▼
取得 CLASSROOM_NAME
「取得所選教室名稱」
        │
        ▼
Intent
「傳遞教室名稱」
        │
        ▼
AllClassroomsSchedule.kt
「顯示該教室的完整課表」

Hermes Agent 幫我檢查出的重點

AllClassroomsAdapter 是所有教室狀態總覽頁的 RecyclerView Adapter。

  • 它接收從 AllClassroomsActivity 傳來的 List<ClassroomInfo>,每一筆資料會顯示成一張 item_all_classrooms.xml 卡片。卡片上會顯示教室名稱、教室類型與是否可飲食。
  • ViewHolder.bind() 會把 classroom.name 填到名稱欄,把 classroom.type 從英文代碼轉成中文顯示,並根據 classroom.foodAllowed 顯示「是」或「否」。
  • 當使用者點擊卡片時,Adapter 會建立前往 ClassroomScheduleDetailActivity 的 Intent,並用 CLASSROOM_NAME 傳出教室名稱,讓下一頁知道要顯示哪一間教室的完整課表。

這個檔案帶出的維護觀察

  1. 職責分離不明確:點擊卡片跳轉的邏輯直接寫死在 Adapter 內部,應改用 Lambda Callback 拋回給 Activity 處理,Adapter 則純粹專注於「資料渲染」,。
  2. 資料模型(UI Model)宣告位置錯置:ClassroomInfo 是 Activity 與 Adapter 之間傳遞資料的通用模型,卻被宣告在 Adapter 檔案內。建議獨立拆分成專屬的 Model 檔案(如 ClassroomInfo.kt),讓資料結構更乾淨且方便全域複用。
  3. 類型轉換邏輯散落各處:將 "Normal" 轉為 "一般教室" 的 when 判斷在多個頁面重複出現。如果日後要新增一種教室類型,就必須逐一翻找修改;應統一收斂為 RoomType 列舉(Enum)集中管理。
  4. 列表重繪暴力:每次資料有微小變動就呼叫 notifyDataSetChanged(),會強制整個列表打掉重繪,既耗費效能又失去換頁動畫。未來資料量擴大時,應升級為官方推薦的 ListAdapter 搭配 DiffUtil,達成精準的局部刷新。

小結

AllClassroomsAdapter 讓「所有教室狀態」頁真正變成一個可讀的總覽。不同於前面的 ClassroomAdapter 只顯示教室名稱,這裡每張卡片會顯示教室名稱、教室類型與是否可飲食,讓管理者不用點進去就能先看到基本資訊。

這個 Adapter 也負責點擊跳頁。當管理者點擊某一間教室時,它會把 CLASSROOM_NAME 傳給 ClassroomScheduleDetailActivity,下一頁再根據這個名稱查出完整課表。這讓整條瀏覽流程串起來:總覽所有教室 → 點選一間 → 查看完整課表。

閱讀這個檔案時,也能看到幾個可維護性問題:Adapter 直接處理頁面跳轉、ClassroomInfo 放在 Adapter 檔案裡、教室類型中英文轉換邏輯與其他頁重複,以及 notifyDataSetChanged() 的整體刷新比較粗略。這些都不是立即錯誤,但很適合放進後續改善清單。

一句話重點總結此 Adapter:

AllClassroomsAdapter 把 ClassroomInfo 顯示成教室狀態卡片,並在點擊卡片時把 CLASSROOM_NAME 傳給 ClassroomScheduleDetailActivity。


下一篇預告:完整課表頁為什麼顯示的是有課時段,而不是空教室?

拆解完所有教室狀態頁的列表 Adapter 之後,點擊某個教室卡片後,下一個無庸置疑我們要進入到「單一教是完整課表頁」來查看一週的有課時段。

明明這是一支「找空教室」的 App,為什麼點進完整課表後,畫面上標示的反而是「有課時段」? 這和邏輯以及管理者閱讀每週課表矩陣時的視覺直覺有著很大的關係。

下一篇,我們將深入剖析完整課表頁- AllClassroomsSchedule.kt ,理解單一教室完整課表頁如何接收 CLASSROOM_NAME,並將代表有課時段的 "X" 呈現在一週課表畫面上。


上一篇
Day 23 | 如果我是管理者,我怎麼一次看懂所有教室狀態?
下一篇
Day 25 | 完整課表頁為什麼顯示的是有課時段,而不是空教室?
系列文
從 Vibe Coding 到可維護專案:用 Hermes Agent 重讀 RoomRush Android 專題 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言