今天我們要來實作 Kimoji App 裡多選標籤的功能。我們會把勾選的狀態和邏輯放到 ViewModel
。透過讓 ViewModel
管理所有狀態,可以讓程式碼更簡潔,也更容易測試。
此系列文章是以我的業餘專案: Kimoji 作為範例。
這款以純 Jetpack Compose 撰寫的 side project,已經在 Google Play 上架。 歡迎試玩!
立馬下載 限免兌換碼
Label
data model,以便讓它儲存勾選狀態,並把預設值設為 false
。data class Label(
val id: Long,
val name: String,
var checked: Boolean = false
)
ViewModel
中實作 changeLabelChecked
函式,用這個函式接收要修改的標籤,並用的新值去修改標籤勾選狀態。class DiaryViewModel : ViewModel() {
...
fun changeLabelChecked(label: Label, checked: Boolean) =
labels.find { it.id == label.id }?.let { label ->
label.checked = checked
}
}
DiaryScreen
提供 ViewModel
的 changeLabelChecked
函式給「標籤清單」 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)
}
)
}
}
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) }
)
}
}
}
LabelItem.kt
檔案。這個檔案內有個 composable function:會根據 LabelList
提供的 checked
state 來決定 borderColor
和 backgroundColor
。@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
)
}
}
}
這是因為 Compose 只會觀察 MutableList
有沒有新增及移除 element。所以 Compose 無法發現 item 內的值有變化 (在這裡是 checkedState
),除非我們叫 Compose 也要觀察這些內容。
我們將在明天的文章中,繼續探討解決方法。
此系列文章是以我的業餘專案:Kimoji 為範例。
Kimoji 是一款心情日記 App,讓你用可愛的 emoji 來撰寫你的心情日記。現在就來試試這款設計精美的微日記吧!
立馬下載 限免兌換碼
Reference: https://developer.android.com/codelabs/jetpack-compose-state