配合分支:day05/start,Repo: 按我
昨天飆出來的程式,好像怪怪的仔細看會發現新增的日記:標題和內容反了。
問題出在哪?繼續昨天的流程,來梳理新增照片的過程發生了什麼事情。

在程式跑的時候,最重要的東西,就是資料會怎麼流動。包含:
昨天做的「查證」,就是在梳理資料流。
通常一個功能壞掉的時候,工程師都會假設哪邊出了問題,久而久之越來越厲害就會變成通靈,非常的玄妙。
先確立幾個假設,過程之中再一齊來進行查證作業:
DiaryEntry 或卡片 UI 有壞掉?簡單粗暴的推測,立刻來看 DiaryEntry 相關的程式碼。從昨天知道的資料流來推測:

進入點 MainActivity 建立時呼叫 onCreate(),並且把 PhotoDiaryApp()叫起來(如果不太熟悉,請複習 Day04 的內容)。昨天知道 PhotoDiaryApp() 用 if/else 來控制 UI 的長相。
這次的目標是看「卡片列表」,也就是 isAddingDiary 非新增日記 狀態,往下找就可以看到 DiaryList()。
DiaryList 可以傳入一個 DiaryEntry 的列表(List),看起來日記的 基本資訊 都寫在裡頭。先來看看 DiaryList:

發現 LazyColumn 裡頭包著 DiaryCard 這應該就是這次要找的。

LazyColumn 元件可以重複印出行,看看裡頭有一個 items() 代表 每一個物件都會執行後面的 code 區塊 也就是印出 DiaryCard()。
每一個 entry 會從 entries 之中拿出來。透過 Android Studio 來看,entry 是一個 DiaryEntry

從 DiaryCard 拿出 entry.title 以及 entry.note 的地方,看不出有放錯 🤔 ,的確是 title 放上面,note 放下面。噢對了,entry.title 可以從 entry 的 title 取值。
這個假設「不成立」
不然就是打字的時候,存標題和內文的「變數」存錯了?(同樣,變數在 Day04 有做介紹)
PhotoDiaryApp() 用 if/else 來控制 UI 的長相。 這次的目標是看「新增照片」的表格 UI,也就是 isAddingDiary 新增日記 狀態,往下找看到 DiaryForm()。

DiaryForm() 接受兩個 callback function,一個是「按下儲存」的時候,會將兩個 String 帶回來。
文字的型別(Type),通常在 Android/Kotlin 的世界裡,String 用來存「文字」。順帶一提,如果存一個「字元」例如:abc 是文字,a 是一個字元。
存字元可以用 Char。(雖然實務上絕大部分的情況都能用 String 打天下)

接著來看變數的部分,看到標題和內容,onValueChange 也就是輸入框值改變的時候,會跑的程式區塊。title = it 和 note = it 。 it 是變數,就是這個 value 發生改變的時候的「值」。讀者可能會注意到前述提到 LazyColumn 的 items(entries) {} 也是類似的東西。
這是 Kotlin 的語法糖(少寫 code 的好東西 :-P)
items(entries) { entry -> // <== 這裡可以自訂這個 it 的名稱
DiaryCard(entry)
}
否則不給名字,也可以寫成:
items(entries) {
DiaryCard(it)
}
會拿到一樣的變數結果。
從這邊看起來,變數存對。var title 和 var note 分別被標題和內容的 TextField onValueChange callback 更新。那會不會是按下「儲存」按鈕,回錯?

看一下「儲存」按鈕按下去 onClick 後,onSave Function 被叫,同時把 title 和 note 帶回去。
防止忘記,onSave Type 是:(String, String) -> Unit 可以塞兩個 String 傳回去。
這裡看起來也是對⋯ 通常這種時候就好想下班⋯ 好想逃離這一切。
但就像那個挖礦後面有鑽石的圖,答案可能呼之欲出囉。
DiaryForm() 把變數回覆給上面叫它的人 (回去 PhotoDiaryApp 程式區塊),做了什麼呢?

先看 onSave = 這一塊,還記得 onSave 的 Type 接受兩個 String 嗎?從大括號後面的兩個變數,就是 依照這個順序取出來的,所以第一個值是 title,第二個值是 note,往回看,的確沒有傳錯。
仔細一看會發現,小丑竟是我自己!? 🤡
新建一個 DiaryEntry 的時候竟然放反了 =.=
DiaryForm(
onSave = { title, note ->
entries = listOf(
DiaryEntry(
photoResId = R.drawable.diary_default,
title = note, // <== =.=
note = title, // <== =.=
mood = "平靜",
createdAt = LocalDateTime.now()
)
) + entries // <== 塞到本來的列表裡
isAddingDiary = false
}
)
太瞎了,不過老實說在實務上還真的就常有這種瞎事發生。(天啊,好想去渡假⋯)
Day05 的修正,只需要把 title 和 note 放到對應的地方即可。
恭喜大家,這大概就是工程師的日常了!瘋狂的通靈找問題算是工作的一環,
現在還沒洗頭,還來得及!(開玩笑的)
DiaryList 吃的是一串 DiaryEntry;日記的基本資訊都在這份列表裡,卡片畫面是照它印出來的。items():LazyColumn 用來重複印出行;items() 代表每個物件都跑一次後面的區塊,所以每個 entry 都會變成一張 DiaryCard。entry.title、entry.note 是從那一筆日記拿出標題和內容;查卡片時先確認這裡有沒有放反呵呵。DiaryForm 接受兩個 callback Function;「按下儲存」時會把兩個 String 帶回去給叫它的人。a);實務上大多還是用 String。it(語法糖):沒幫 callback function/lambda 參數取名時,值就叫 it;onValueChange 的 title = it,和 items(entries) { DiaryCard(it) } 是同一套少寫 code 的寫法。(String, String) -> Unit:onSave 可以塞兩個 String 傳回去;大括號後的兩個變數是依這個順序取出來的。DiaryEntry 的欄位:新建日記時 title =、note = 要接到對的值;今天的 bug 就是這兩個放反了。照片中紅衣服的人是誰啊
你想知道嗎?告訴你一個活動,每個月的第二個星期二的晚上的 TOOCON,可以找到他