配合分支:day10/start,Repo: 按我
直接進入業配主題,為什麼會刪錯呢?先提前回顧畫面上的日記是從哪裡印出來的呢?是跟 ViewModel 透過一個方法取得的。

這種只為了取得某種東西的方法,實務上通常稱之為 getter。(反之,單純設定數值的則稱為 setter)。這個 getter 方法在 isNewestFirst 為真時,返回「排序」後的列表,但是,原來的 entries 被排序了嗎?也就是黏在 ViewModel 上面的:
var entries by mutableStateOf(sampleEntries())
答案是,沒有!
這個 getter 單純幫我們排過,但是原資料還是「排序前的版本」。所以刪除時位置才會對不起來。除非回傳的是非 isNewestFirst 的版本。
所以,位置並不能作為一個準確知道「是哪個日記」的資訊,除非更動位置時,也把上面黏著的 entries 也改成排序過的版本。
但這個不是最棒的解法,也就是幫每一個日記,提供一個專屬於他的名牌,通常叫做 id(這種東西叫做 Key)。
因為 id 不會重複,每次要求更動資料,無論順序怎麼更動,都可以調整到正確的日記。
直接去更動照片日記的 data class:DiaryEntry。這個 class 記錄著日記的屬性。在這,幫它加入一個新的屬性: id。
data class DiaryEntry(
val id: String = UUID.randomUUID().toString(), // 👈🏼 加入
val photoResId: Int,
val title: String,
val note: String,
val mood: String,
val createdAt: LocalDateTime
)
他的寫法好像有點叛逆,為何後面有 =?
在 Koltin 之中,這叫做預設值(Default Value),如果建構子(Constructor) 沒有宣告,則直接套用預設值。
例如車子的例子:
data class Car(
val engine: Int = 1800
val brand: String = "smart"
val nickName: String
)
當直接 init 一個新的 Car() 但什麼都不給的時候:
val car = Car(nickname = "fort") // 👈🏼 什麼都不給,但 nickname 沒有預設值,要宣告
car.engine // => 會 = 1800
car.brand // => 會是 smart
回到照片日記,每次產生一個日記,就建立一個隨機的 UUID(就是隨機的一串字啦,文件)。把這行字當成每一則日記的 唯一值。
deleteDiaryAt() 再也不需要透過位置來移除,現在每一則日記都有自己唯一的 id。現在透過 id 來進行刪除:

印出所有的照片日記的列表,程式碼可以調整:

改成「只需要回傳目前的 DiaryEntry id」就好,ViewModel 取得 id 後,刪除該 id 的相片日記。
然後 itemsIndexed 也不需要了,我們不用日記的 Index 了,改回 items。
上面呼叫 DiaryList 的地方應該會 panic,所以上去調整一下程式碼。


果然成功了,不過目前的處理方式有個問題,其實目前並不能保證 id 不會重複,只要重新開啟 App,id 就變了⋯
isNewestFirst 為真時只回傳排過的列表,黏在 ViewModel 上的 entries 仍是排序前版本。DiaryEntry 加入 id,讓每一則日記自己帶著名牌。=;建構子沒宣告這個參數時,就直接套用這個值。itemsIndexed 改回 items