Jetpack Compose LazyColumn - 如何单独更新每个项目的值?

Sev*_*nds 1 android-jetpack-compose lazycolumn

我正在为我的应用程序开发购物车功能。我希望分别添加/减少 LazyColumn 中每个列表项的数量。我只使用一个“记住”,因此如果我单击添加/减少,它们都会同时更新。如何单独控制每个项目?

截屏

@Composable
fun InventoryCartScreen(
    mainViewModel: MainViewModel = hiltViewModel()
) {

    val multiSelectValue = mutableStateOf(0)// This is the value I want to change

//random list
val shopList = listOf(
        ShoppingList(id = 0,itemNumber = "1",itemDescription = "1",currentInventory = 0,optimalInventory = 0,minInventory = 0),
        ShoppingList(id = 0,itemNumber = "2",itemDescription = "2",currentInventory = 0,optimalInventory = 0,minInventory = 0)
    )

    Column(...) {
       LazyColumn(...) {
            items(items = shopList, key = { it.id }) { item ->
                InventoryCartScreenContents(
                    onaddClick= { multiSelectValue.value ++ }, //adds for all
                    onDecreaseClick = { multiSelectValue.value -- }, //decreases for all
                    value = multiSelectValue.value //setting the initial value for all
                )

            }

        }
    }
}

Run Code Online (Sandbox Code Playgroud)

以下是可帮助您重现问题的可组合内容。

@Composable
fun InventoryCartScreenContents(
    onAddClick: (Int) -> Unit,
    onDecreaseClick: () -> Unit,
    value: Int,
) {
           Row(...) {
                Button(
                    onClick = { onAddClick(itemId) }
                ) {
                    Text(text = "+")
                }
               
                Button(
                    onClick = onDecreaseClick
                ) {
                    Text(text = "-")
                }
               
                }

            }

Run Code Online (Sandbox Code Playgroud)

Ric*_*per 12

在您的mutableStateListOf(...). 现在,从您想要读取它的可组合项(即您的.mutableStateOf(listOf(...))ViewModelLazyColumn

在您的 中ViewModel,您可以根据需要设置值,并且它们将在更新时在可组合项中更新。现在,可组合项(即您的列)可以使用列表项的索引作为惰性列项的索引。因此,您可以在视图模型中为不同的项目存储不同的数据,并且它会正常工作。

问题似乎是您在这里缺少状态提升的概念。我以为我有一些很好的帖子解释了它,但似乎是我发布的最好的帖子。不管怎样,我建议检查这个官方参考资料,因为我基本上就是在那里找到的(可以说,有一点猎头。)

基本思想是所有内容都存储在 viewModel 中。然后,将该状态分为“setter”和“getters”,并将它们沿层次结构传递。

例如,ViewModel 可能有一个名为 的项目text,好吗?

class VM : ViewModel(){
 var text by mutableStateOf("Initial Text")
 private set // This ensures that it cannot be directly modified by any class other than the viewmodel
 fun setText(newText: Dtring){ // Setter
  text = newText
 }
}
Run Code Online (Sandbox Code Playgroud)

text如果您希望在单击按钮时更新 的值,则可以通过以下方式将该按钮与 viewModel 连接起来

MainActivity{
 onCreate{
  setContent{
   StateButton(
    getter = viewModel.text, // To get the value
    setter = viewModel::setText  // Passing the setter directly
   )
  }
 }
}
Run Code Online (Sandbox Code Playgroud)

在您的 Button Composable 声明中

@Composable
private fun ComposeButton(
getter: String,
setter: (String) -> Unit // (receive 'String') -> return 'Unit' or 'void'
){
 Button(
 onClick = { setter("Updated Text") } // Calls the setter with value "Updated Text", updating the value in the viewModel
 ){
   Text(text = getter)
 }
}
Run Code Online (Sandbox Code Playgroud)

此按钮从 viewModel 读取值“get”,即,它读取 的值text,因为它从模型传递到可组合项。然后,当我们从按钮(在 中onClick)收到单击事件时,我们调用作为可组合项参数收到的 setter,并使用新值调用它,在本例中为“更新的文本”,这将一直向上到 viewModel,并更改text其中的值。

现在,text最初通过在初始化中使用来初始化为状态持有者mutableStateOf(...),这将在其值发生更改时触发重组。现在,由于值实际上确实发生了变化(从“初始文本”到“更新文本”),因此将在读取变量值的所有可组合项上触发重组text。现在,我们的ComposeButtonComposable 确实读取了 的值text,因为我们直接将其传递给getter该 Composable 的参数,对吗?因此,所有这些都将产生一个可组合项,它将从层次结构(视图模型)中的单个点读取值,并且仅在该值更改时更新。因此,Viewmodel 充当可组合项的单一事实来源。

运行此命令时,您将得到一个可组合项,该可组合项最初显示为“初始文本”,但当您单击它时,它会更改为“更新的文本”。我们在 getter 和 setter 的帮助下将可组合项连接到主 viewModel。当可组合项接收事件时,我们调用从模型接收的 setter,这些 setter 将链继续向上至 viewModel,并更改模型内变量(状态持有者)的值。然后,可组合项已经通过“getters”读取这些变量,因此,它们重新组合以反映更新的值。这就是状态提升。所有状态都存储在 viewModel 中,并传递给可组合项。当值需要更改时,可组合项将“事件”向上传递到 viewModel(沿层次结构向上),然后在更新值时,更新后的状态将向下传递到可组合项(“状态”沿层次结构向下流动) 。

您所需要的只是使用此方法,但带有一个列表。您可以使用项目的索引来跟踪项目,并在 viewModel 中更新它们的属性,就像本示例演示的更新 的值一样a。您可以将项目的所有属性存储在单个列表中。

只需创建一个数据类,就像这样

data class Item(p1: ..., p2:..., p3 : ...){}
Run Code Online (Sandbox Code Playgroud)

然后,val list by mutableStateOf(listOf<Item>())

清除?

好的,这是针对您的用例的具体解释。

您的代码似乎太大了,但这是我得到的:

惰性列中有两个项目,每个项目都有三个按钮。你的问题是关于两个按钮,增加和减少。您希望按钮仅修改它们所属项目的属性,对吧?

好吧,再次使用状态提升,如上所述

ViewModel{
 val list by mutableStateOf(listOf<Item>()) // Main (central) list
 val updateItem(itemIndex: Int, item: Item){
   list.set(itemIndex, item) // sets the element at 'itemIndex' to 'item' 
 } // You can fill the list with initial values if you like, for testing
}
Run Code Online (Sandbox Code Playgroud)

创建 Getter 和 Setter 后,您将使用它们来读取和更新整个项目,即使您必须修改它们的单个属性。我们可以使用便利的方法来copy()让我们的生活更轻松。

MainScreen(){
 //Assuming here is the LazyColumn
 list = viewModel.list // Get the central list here
 LazyColumn(...){ // Try to minimize code like this in your questions, since it does not have to compile, just an illustration.
  items(list){ item ->
   // Ditch the multiSelectValue, we have the state in the model now.
   Item( // A Composable, like you demonstrated, but with a simpler name for convenience
     item: Item,
     onItemUpdate: (Item) -> Unit // You don't need anything else
   )
  }
 }
}
Run Code Online (Sandbox Code Playgroud)

只需创建一个数据类并将所有内容存储在其中即可。我演示的类Item(不是可组合类)是数据类。它可能有这样的值

data class Item(var quantity: Int, ...){} // We will increase/decrease this
Run Code Online (Sandbox Code Playgroud)

现在,在您的Item可组合项中,您收到“添加”事件(在onClick“添加”按钮中),只需使用设置器更新值,如下所示

onClick = {
 updateItem(
  item // Getter
      .copy( //Convenience Method
        quantity = item.quantity + 1 // Increase current Quantity
      )
 )
}
Run Code Online (Sandbox Code Playgroud)

只需对减少和删除 ( ) 执行相同的操作updateItem(0),您就应该很好地完成此操作...最后。如果您还有任何疑问,请尽管询问。如果没有任何效果,我可以通过视频通话提供帮助。