iT邦幫忙

2026 iThome 鐵人賽

DAY 10
2

配合分支:day10/start,Repo: 按我

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

https://ithelp.ithome.com.tw/upload/images/20260829/20141615ndUlxmJJAi.png

查證

這種只為了取得某種東西的方法,實務上通常稱之為 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(就是隨機的一串字啦,文件)。把這行字當成每一則日記的 唯一值

調整 ViewModel 刪除邏輯

deleteDiaryAt() 再也不需要透過位置來移除,現在每一則日記都有自己唯一的 id。現在透過 id 來進行刪除:

https://ithelp.ithome.com.tw/upload/images/20260829/201416152dqZ9L3vnb.png

UI 程式碼調整

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

https://ithelp.ithome.com.tw/upload/images/20260829/201416150RMvn9ipwo.png

改成「只需要回傳目前的 DiaryEntry id」就好,ViewModel 取得 id 後,刪除該 id 的相片日記。
然後 itemsIndexed 也不需要了,我們不用日記的 Index 了,改回 items

上面呼叫 DiaryList 的地方應該會 panic,所以上去調整一下程式碼。

https://ithelp.ithome.com.tw/upload/images/20260829/20141615gEqEzmSk23.png

結果

果然成功了,不過目前的處理方式有個問題,其實目前並不能保證 id 不會重複,只要重新開啟 App,id 就變了⋯

TL;DR

  • getter:只為了取得某種東西的方法(反之設定數值的叫 setter);今天畫面上的日記就是從 ViewModel 這個 getter 拿來印的。
  • 原資料沒被排序:getter 在 isNewestFirst 為真時只回傳排過的列表,黏在 ViewModel 上的 entries 仍是排序前版本。
  • 位置不能當身份:畫面列表和原資料順序不同,用位置刪除就會對錯日記。
  • id(Key):每一則日記專屬的名牌;因為不會重複,無論順序怎麼更動都能調到正確的日記。
  • data class 的屬性:在 DiaryEntry 加入 id,讓每一則日記自己帶著名牌。
  • 預設值(Default Value):屬性後面的 =;建構子沒宣告這個參數時,就直接套用這個值。
  • 更多的建構子(Constructor)補充:用來建立物件;沒有預設值的參數一定要給,有預設值的可以省略。
  • UUID:每次產生日記就建立一串隨機字,當成該則日記的唯一值。
  • 用 id 刪除:不再靠位置移除,UI 只回傳目前的 DiaryEntry id,並把 itemsIndexed 改回 items

上一篇
[Day09] 加入刪除日記的功能
下一篇
[Day11] Local persistence
系列文
我的第一個手作りAndroid App!快樂學習物件導向程式設計!11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
chiaominchang222
iT邦新手 5 級 ‧ 2026-08-29 20:51:14

請謝謝對不起(?

我要留言

立即登入留言