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

Posted

技术标签:

【中文标题】Jetpack Compose:更新列表元素内容时不会发生重组【英文标题】:Jetpack Compose: No recomposition happening, when updating list element contents 【发布时间】:2021-12-11 02:56:00 【问题描述】:

我正在尝试使用 android 的 Jetpack Compose。 对于简单的用例,一切都按预期工作, 但对于更高级的案例,我遇到了一些缺少重组的问题。 这是我在 *** 上的第一篇文章。我希望我能很好地描述我的问题以获得一些帮助。 ;)

我的模型

我正在模拟成分的存储系统,其中

成分由名称和可选图标组成:
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.stockx")
            
        
    

我的视图模型:

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

我想向你们学习:

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

【问题讨论】:

不确定如何将其组合在一起。我的成分清单现在应该采用:storageItems: State&lt;List&lt;StorageItem&gt;&gt; 吗?我现在是否在我的成分列表中使用items(storageItems.value)?试过了,也不管用,但可能我没有完全按照你的意思做。 我刚刚在这里回答了一个类似的问题:***.com/a/69718724/753632 我查看了您的其他帖子。非常感谢您的解决方案。不过,使用随机值来更新 LiveData 对我来说并不是特别干净。现在,我想我会坚持使用 mutableStateList 直到找到完美的解决方案:D 目前的肮脏解决方法:storageItems.add(StorageItem(Ingredient("", 0), 0))storageItems.removeLast() 【参考方案1】:

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

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

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)
    


【讨论】:

这听起来很有希望,非常感谢!如果我理解正确,那么这将使我无法动态添加存储项目,因为 storageItems 现在是不可变的? 要添加一个项目,你可以做storageItems += newItem。请参阅this 示例。

以上是关于Jetpack Compose:更新列表元素内容时不会发生重组的主要内容,如果未能解决你的问题,请参考以下文章

底部导航栏与 Jetpack Compose 中的屏幕内容重叠

Jetpack Compose | Compose 滑动列表真的需要使用LazyColumn吗?No No No!

Jetpack Compose | Compose 滑动列表真的需要使用LazyColumn吗?No No No!

Jetpack Compose | Compose 滑动列表真的需要使用LazyColumn吗?No No No!

jetpack compose 上的垂直列表中的水平列表不顺畅

Jetpack Compose 数字输入