android - Jetpack Compose:更新列表元素内容时不会发生重组
问题描述
我正在尝试使用 Android 的 Jetpack Compose。
对于简单的用例,一切都按预期工作,但是对于更高级的用例,
我在缺少重组时遇到了一些麻烦。
这是我在 stackoverflow 上的第一篇文章。我希望我能很好地描述我的问题以获得一些帮助。;)
我的模型:
我正在模拟成分的存储系统,其中
- 成分由名称和可选图标组成:
data class Ingredient(val name: String, @DrawableRes val iconResource: Int? = null)
- 一个StorageItem由一种成分和一种库存(该成分的存储量)组成:
data class StorageItem(val ingredient: Ingredient, var stock: Int)
我的可组合物:
我的StorageUi组合应该列出所有存储项目
,并显示成分的图标和名称,以及库存。
对于这篇文章,我去掉了所有不相关的修饰符和格式以简化可读性。
(请注意,我用第二个没有视图模型
的版本重载了我的 StorageScreen 组合,以便于测试并促进 Android Studio 中的预览功能。)
@Composable
fun StorageScreen(viewModel: StorageViewModel) {
StorageScreen(
navController = navController,
storageItems = viewModel.storageItems,
onIngredientPurchased = viewModel::purchaseIngredient
)
}
@Composable
fun StorageScreen(storageItems: List<StorageItem>, onIngredientPurchased: (StorageItem) -> Unit) {
Column {
TitleBar(...)
IngredientsList(storageItems, onIngredientPurchased)
}
}
@Composable
private fun IngredientsList(storageItems: List<StorageItem>, onIngredientPurchased: (StorageItem) -> Unit) {
LazyColumn {
items(storageItems) { storageItem ->
IngredientCard(storageItem, onIngredientPurchased)
}
}
}
@Composable
private fun IngredientCard(storageItem: StorageItem, onIngredientPurchased: (StorageItem) -> Unit) {
Card(
Modifier.clickable { onIngredientPurchased(storageItem) }
) {
Row {
ImageIcon(...)
Text(storageItem.ingredient.name)
Text("${storageItem.stock}x")
}
}
}
我的视图模型:
在我的 ViewModel 中,我
- 创建一个可变状态列表(此处未显示数据的初始化)
- 如果用户点击成分卡,则提供增加库存的事件处理程序
class StorageViewModel : ViewModel() {
var storageItems = mutableStateListOf<StorageItem>()
private set
fun purchaseIngredient(storageItem: StorageItem) {
storageItem.stock += 1
}
}
问题:改变原料的库存时不会发生重组
我尝试更改事件处理程序以简单地从列表中删除被点击的项目:
fun purchaseIngredient(storageItem: StorageItem) {
storageItems.remove(storageItem)
}
瞧,UI 重新组合,轻拍的成分消失了。
我学到的是:
- mutableStateListOf() 确实观察到列表的变化(添加、删除、重新排序)
- mutableStateListOf() 不会观察到列表中元素的变化(成分名称/图标/库存变化)
我想从你们那里学到什么:
- 你将如何解决这个问题?
- 如果列表中的任何元素更改其状态,我该怎么做才能实现重组?
解决方案
如果列表中的任何元素更改其状态,我该怎么做才能实现重组?
仅当您更改列表本身时才会发生重组。你可以这样做。
class StorageViewModel : ViewModel() {
var storageItems by mutableStateOf(emptyList<StorageItem>())
private set
fun purchaseIngredient(storageItem: StorageItem) {
storageItems = storageItems.map { item ->
if(item == storageItem)
item.copy(stock = item.stock + 1)
else
item
}
}
}
由于这是一个非常常见的操作,因此您可以创建一个扩展函数以使其看起来更好一些。
fun <T> List<T>.updateElement(predicate: (T) -> Boolean, transform: (T) -> T): List<T> {
return map { if (predicate(it)) transform(it) else it }
}
fun purchaseIngredient(storageItem: StorageItem) {
storageItems = storageItems.updateElement({it == storageItem}) {
it.copy(stock = it.stock + 1)
}
}
推荐阅读
- postgresql - Postgres:SELECT FOR UPDATE 在锁定释放后看不到新行
- kotlin - 未调用 ktor 中的应用程序级事件
- java - 无法将扫描仪转换为文件,扫描仪位于文件末尾,fileNotFoundException
- java - 在 IDEA 中,如何自动删除 .attach_pidxxx 文件
- swift4 - 如何处理标准和自定义 Swift 4 Decodable 属性的混合?
- r - 平均(或任何其他统计)计算省略零
- javascript - 从 href="path/of/file..WAV" 在 VLC 中打开文件
- reactjs - 如何从另一个用于 SSO 的 UI 应用程序获取 React 应用程序中的密钥(访问令牌)
- javascript - 如何使用 JS 打开一个 url 链接而不是嵌入“a”标签中的文本或图像
- java - jsf中如何使用I18n翻译异常信息