Jetpack Compose:更新列表元素内容时不会发生重组

Tob*_* M. 8 android kotlin android-viewmodel android-jetpack-compose lazycolumn

我正在尝试 Android 的 Jetpack Compose。
\n对于简单的用例,一切都按预期工作,
\n但我在缺少更高级案例的重组方面遇到了一些麻烦。

\n

我的型号

\n

我正在模拟一个原料存储系统,其中

\n
    \n
  • 成分由名称和可选图标组成
  • \n
\n
data class Ingredient(val name: String, @DrawableRes val iconResource: Int? = null)\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • StorageItem由成分和库存(该成分的存储量)组成
  • \n
\n
data class StorageItem(val ingredient: Ingredient, var stock: Int)\n
Run Code Online (Sandbox Code Playgroud)\n

我的可组合项

\n

我的StorageUi可组合项应该列出所有存储项目
\n并显示成分的图标和名称以及库存。
\n对于这篇文章,我删除了所有不相关的修饰符和格式以简化可读性。
\n(请注意,我使用没有视图模型的第二个版本重载了 StorageScreen 可组合项,
\n是为了更轻松地进行测试并促进 Android Studio 中的预览功能。)

\n
    @Composable\n    fun StorageScreen(viewModel: StorageViewModel) {\n        StorageScreen(\n            navController = navController,\n            storageItems = viewModel.storageItems,\n            onIngredientPurchased = viewModel::purchaseIngredient\n        )\n    }\n\n    @Composable\n    fun StorageScreen(storageItems: List<StorageItem>, onIngredientPurchased: (StorageItem) -> Unit) {\n        Column {\n            TitleBar(...)\n            IngredientsList(storageItems, onIngredientPurchased)\n        }\n    }\n\n    @Composable\n    private fun IngredientsList(storageItems: List<StorageItem>, onIngredientPurchased: (StorageItem) -> Unit) {\n        LazyColumn {\n            items(storageItems) { storageItem ->\n                IngredientCard(storageItem, onIngredientPurchased)\n            }\n        }\n    }\n\n    @Composable\n    private fun IngredientCard(storageItem: StorageItem, onIngredientPurchased: (StorageItem) -> Unit) {\n        Card(\n            Modifier.clickable { onIngredientPurchased(storageItem) }\n        ) {\n            Row {\n                ImageIcon(...)\n\n                Text(storageItem.ingredient.name)\n\n                Text("${storageItem.stock}x")\n            }\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

我的视图模型:

\n

在我的 ViewModel 中,我

\n
    \n
  • 创建一个可变状态列表(使用此处未显示的数据进行初始化)
  • \n
  • 如果用户点击成分卡,则提供增加库存的事件处理程序
  • \n
\n
    class StorageViewModel : ViewModel() {\n\n        var storageItems = mutableStateListOf<StorageItem>()\n            private set\n\n        fun purchaseIngredient(storageItem: StorageItem) {\n            storageItem.stock += 1\n        }\n\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

问题:更改原料库存时不会发生重组

\n

我尝试更改事件处理程序以简单地从列表中删除点击的项目:

\n
        fun purchaseIngredient(storageItem: StorageItem) {\n            storageItems.remove(storageItem)\n        }\n
Run Code Online (Sandbox Code Playgroud)\n

然后瞧\xc3\xa0,UI 重新组合,点击的成分消失了。

\n

我学到的是:

\n
    \n
  • mutableStateListOf() 确实观察列表的更改(添加、删除、重新排序)
  • \n
  • mutableStateListOf() 不会观察列表中元素的更改(成分名称/图标/库存更改)
  • \n
\n

我想向你们学习的是:

\n
    \n
  • 您将如何解决这个问题?
  • \n
  • 如果列表中的任何元素更改其状态,我该怎么做才能实现重组?
  • \n
\n

Arp*_*kla 2

如果列表中的任何元素更改其状态,我该怎么做才能实现重组?

仅当您更改列表本身时才会发生重组。你可以这样做。

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
        }
     }
}
Run Code Online (Sandbox Code Playgroud)

由于这是一个非常常见的操作,因此您可以创建一个扩展函数以使其看起来更好一些。

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)
    }
}

Run Code Online (Sandbox Code Playgroud)

  • 要添加项目,您可以执行“storageItems += newItem”。请参阅[此](https://pl.kotl.in/jKbbV60w9)示例。 (3认同)