iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0

https://ithelp.ithome.com.tw/upload/images/20220912/20151958IFjRs0xIZ4.png Medium 好讀版

今天我們要來實作 Kimoji App 裡多選標籤的功能。我們會把勾選的狀態和邏輯放到 ViewModel。透過讓 ViewModel 管理所有狀態,可以讓程式碼更簡潔,也更容易測試。

此系列文章是以我的業餘專案: Kimoji 作為範例。
這款以純 Jetpack Compose 撰寫的 side project,已經在 Google Play 上架。 歡迎試玩!

https://ithelp.ithome.com.tw/upload/images/20220907/20151958vXuPLv4aki.png 立馬下載 https://ithelp.ithome.com.tw/upload/images/20220907/20151958LtM1NGErzK.png 限免兌換碼
  1. 首先定義 Label data model,以便讓它儲存勾選狀態,並把預設值設為 false
data class Label(
    val id: Long, 
    val name: String, 
    var checked: Boolean = false
)
  1. ViewModel 中實作 changeLabelChecked 函式,用這個函式接收要修改的標籤,並用的新值去修改標籤勾選狀態。
class DiaryViewModel : ViewModel() {
   ...
   fun changeLabelChecked(label: Label, checked: Boolean) =
       labels.find { it.id == label.id }?.let { label ->
           label.checked = checked
       }
}
  1. DiaryScreen 提供 ViewModelchangeLabelChecked 函式給「標籤清單」 composable 的 onCheckedLabel。如下所示:
@Composable
fun DiaryScreen(
    modifier: Modifier = Modifier, 
    diaryViewModel: DiaryViewModel = viewModel()
) {
   Column(modifier = modifier) {
       LabelList(
           labels = diaryViewModel.labels,
           onCheckedLabel = { label, checked ->
               diaryViewModel.changeLabelChecked(label, checked)
           }
       )
   }
}
  1. 開啟 LabelList 並在介面上新增一個 onCheckedLabel lambda 函式參數,以便向下傳遞給 LabelItem
@Composable
fun LabelList(
    modifier: Modifier = Modifier,
    labels: List<Label>,
    onCheckedLabel: (Label, Boolean) -> Unit
) {
    LazyVerticalGrid(
        columns = GridCells.Fixed(3),
        modifier = modifier
    ) {
        items(
            items = labels,
            key = { label -> label.id }
        ) { label ->
            LabelItem(
                name = label.name,
                checked = label.checked,
                onCheckedChange = { checked -> onCheckedLabel(label, checked) }
            )
        }
    }
}
  1. 我們來看一下 LabelItem.kt 檔案。這個檔案內有個 composable function:會根據 LabelList 提供的 checked state 來決定 borderColorbackgroundColor
@Composable
fun LabelItem(
    modifier: Modifier = Modifier,
    name: String,
    checked: Boolean,
    onCheckedChange: (Boolean) -> Unit
) {
    val borderColor = if (checked) {
        MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
    } else {
        MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
    }
    val backgroundColor = if (checked) {
        MaterialTheme.colorScheme.primary.copy(alpha = 0.12f)
    } else {
        MaterialTheme.colorScheme.background
    }

    Surface(
        border = BorderStroke(
            width = 1.dp,
            color = borderColor
        )
    ) {
        Row(
            modifier = modifier
                .background(backgroundColor)
                .clickable(onClick = { onCheckedChange(!checked) }),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                modifier = Modifier
                    .weight(1f)
                    .padding(start = 16.dp),
                text = name
            )
        }
    }
}
  1. 現在我們把 app 跑起來,然後嘗試點選任何標籤。結果竟然無法順利選取標籤。

這是因為 Compose 只會觀察 MutableList 有沒有新增及移除 element。所以 Compose 無法發現 item 內的值有變化 (在這裡是 checkedState),除非我們叫 Compose 也要觀察這些內容。

我們將在明天的文章中,繼續探討解決方法。

此系列文章是以我的業餘專案:Kimoji 為範例。

Kimoji 是一款心情日記 App,讓你用可愛的 emoji 來撰寫你的心情日記。現在就來試試這款設計精美的微日記吧!

https://ithelp.ithome.com.tw/upload/images/20220907/20151958vXuPLv4aki.png 立馬下載 https://ithelp.ithome.com.tw/upload/images/20220907/20151958LtM1NGErzK.png 限免兌換碼

Reference: https://developer.android.com/codelabs/jetpack-compose-state


上一篇
將 data 和 event 遷移到 ViewModel
下一篇
讓 Compose 觀察 list item 內值的變化
系列文
Kimoji:以 Jetpack Compose 實作一款「心情日記」應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言