iT邦幫忙

2022 iThome 鐵人賽

DAY 30
0

前言


昨天使用 *toMutableStateList*() 讓 list 新增和移除可以讓畫面更新。

今天想要嘗試更改 list 的參數內容,讓內容也可以讓畫面更新。

讓 List 中的參數帶有狀態


title 原本的 typeString ,改成MutableState<> 就可以有狀態。

/**
 * @param title 訓練名稱
 * **/
data class TrainingItem(
    //...
    val title: MutableState<String> = mutableStateOf(""),
    //...
) 

這個方式還可以改成以下的寫法,在建立物件時比較方便

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
/**
 * @param initTitle 訓練名稱
 * **/
data class TrainingItem(
    //...
    val initTitle: String = "",
    //...
) {
    var title by mutableStateOf(initTitle)
}

Text 改為 TextField


為了方便處理輸入狀態,這裡先改成OutlinedTextField ,利用 onValueChange 來取得改變後的 title 值,lambda 中的 it 就可以取得編輯後的值。

//Text(
//  text = trainingItem.title,
//  modifier = Modifier.weight(1f)
//)
OutlinedTextField(
    modifier = Modifier.weight(1f),
    value = trainingItem.title,
    onValueChange = { /*TODO*/ }
)

為了將這個值傳到上層的 Composable,我在 TrainingItem 新增function參數changeTitle: (String) -> Unit

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingItem(
    trainingItem: TrainingItem,
    changeTitle: (String) -> Unit,
    onClick: () -> Unit
) {

}

再把 functionOutlinedTextField 用,這樣上層就可以拿到 Title String

onValueChange = { changeTitle(it) }

LazyColumn


TrainingItem 藉由 changeTitle 得到 title 就可以使用 viewModel 的 function 還操作

LazyColumn(
    modifier = Modifier
        .padding(padding)
        .padding(horizontal = 8.dp)
) {
    itemsIndexed(trainingList) { i, item ->
        TrainingItem(
            item,
            changeTitle = {
								trainingVM.updateTrainingTitle(i, it)
            },
            onClick = { trainingVM.deleteTraining(i) }
        )
    }
}

一開始我只改了 List 中的 item 的 value

//ViewModel
fun updateTrainingTitle(index: Int, newTitle: String) {
    _trainingList[index].title = newTitle
}

畫面上顯示 Ok

但是當我使用複製最後一個item 時卻發現還是取到舊的值

所以就改成直接把整個 TrainingItem 換掉

//ViewModel
fun updateTraining(trainingItem: TrainingItem) {
    _trainingList[trainingItem.index] = trainingItem
}
//LazyColumn
TrainingItem(
    item,
    changeTitle = {
        trainingVM.updateTraining(item.copy(initTitle = it))
    },
    onClick = { trainingVM.deleteTraining(i) }
)

總結


雖然嘗試讓 List 的參數也能有狀態,然而我在複製List的時候卻沒有取得更新後的狀態的值,也許是狀態沒更新?
截稿時間在即,先用置換整個 List Item 來達到更新,未來再繼續研究。

心得


第一次跑鐵人賽,來呼應第一天我的問題:“30天後筆者的身心狀況會怎麼樣呢?”。
這30天體重維持,反而有burn out 的感覺。連續30天維持運動的強度,卻在每天 00:00 前交稿衝刺 (第一天就把存稿用完了每天都在想新東西,反而睡眠不足了,時間管理有待加強啊。

經過這次的挑戰,也發現時間是擠一擠就會出現的東西!?不知不覺每天多了2~3小時的學習時間,也希望這個習慣能和睡眠時間再調整一下,持續在下班時間充實自己。

感謝團員們的支持,有你們才能在每天凌晨之前熱血衝刺。

今日運動
休息(終於可以好好睡覺了XD


上一篇
Day 29 把 data List 搬到 ViewModel
系列文
今年一定減成功!Jetpack Compose 做出重訓紀錄APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
John Lu
iT邦新手 4 級 ‧ 2022-10-07 00:05:24

上一篇裡,trainingVM expose 出來的 trainingList 型態是 List<TrainingItem>
List<T> 型態本身不可觀察。Compose 只能觀察 State<T> 的變化來 trigger recomposition。
簡單的改法可能是直將型態改成 SnapshotStateList<T

0
brightdanielt
iT邦新手 5 級 ‧ 2023-08-20 16:09:43

有關清單的 item 內容更新後,沒有觸發 recomposition 的議題,在這個 codelab 有討論與解決建議,供您參考
Jetpack Compose 的狀態概念說明

我要留言

立即登入留言