3

我正在尝试使用 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() 不会观察到列表中元素的变化(成分名称/图标/库存变化)

我想从你们那里学到什么:

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

1 回答 1

0

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

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

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

于 2021-10-26T05:50:18.143 回答