【问题标题】:Can't edit the values on the TextFields with values populated from database in Jetpack Compose无法使用从 Jetpack Compose 中的数据库填充的值编辑 TextFields 上的值
【发布时间】:2021-08-16 14:53:49
【问题描述】:

我正在使用 Room 数据库应用程序的编辑屏幕。在获得一条记录并用值填充 TextFields 后,我无法再编辑 TextFields。看起来数据在每次击键时都会被覆盖。我该如何解决这个问题?

    val receivedItem = itemViewModel.getItem(itemId!!.toInt()).observeAsState()
    val item = receivedItem.value ?: Item(0, "", 0.0, 0)

    // if I assign the values here from the database, I get all empty values
    var itemName by remember { mutableStateOf("") }
    var itemPrice by remember { mutableStateOf("") }
    var itemQuantity by remember { mutableStateOf("") }

    // if I wrap these values in LaunchEffect(Unit), they don't get assigned at all
    itemName = item.itemName
    itemPrice = item.itemPrice.toString()
    itemQuantity = item.quantityInStock.toString()

    Column(
        modifier = Modifier
            .padding(horizontal = 32.dp, vertical = 16.dp)
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        OutlinedTextField(
            value = itemName,
            onValueChange = { itemName = it },
            label = { Text("Item name") },
            modifier = Modifier
                .padding(top = 16.dp)
                .fillMaxWidth()
        )

        OutlinedTextField(
            value = itemPrice,
            onValueChange = { itemPrice = it },
            label = { Text("Item price") },
            modifier = Modifier
                .padding(top = 16.dp)
                .fillMaxWidth()
        )

        OutlinedTextField(
            value = itemQuantity,
            onValueChange = { itemQuantity = it },
            label = { Text("Item quantity") },
            modifier = Modifier
                .padding(top = 16.dp)
                .fillMaxWidth()
        )
        ...

感谢您的帮助!

【问题讨论】:

    标签: android kotlin android-jetpack-compose


    【解决方案1】:

    出现的问题是,每次更改 itemName、itemPrice 和 itemQuantity 的值时,可组合项都会重新创建自身以显示数据。这会强制执行代码行

    itemName = item.itemName
    itemPrice = item.itemPrice.toString()
    itemQuantity = item.quantityInStock.toString()
    

    在每次重组时运行,从而重置您的文本字段。一个简单的解决方法是简单地使用从一开始就定义的项目值来定义初始可变状态值,就像这样,删除上面的 3 行。这样,只有在观察到来自视图模型的新值 receivedItem 时,文本值才会被覆盖。

    var itemName by remember { mutableStateOf(item.itemName) }
    var itemPrice by remember { mutableStateOf(item.itemPrice) }
    var itemQuantity by remember { mutableStateOf(item.itemQuantity) }
    

    【讨论】:

    • 谢谢@A.D!在这些行中:val receivedItem by itemViewModel.getItem(itemId!!.toInt()).observeAsState() val item = receivedItem ?: Item(0, "", 0.0, 0) Log.d("EditScreen", "Editing item: $item") 我得到的项目还好:D/EditScreen: Editing item: Item(id=2, itemName=Banana, itemPrice=0.3, quantityInStock=250)
    • 但是当我按照您的建议记住这些项目时:var itemName by remember { mutableStateOf(item.itemName) } var itemPrice by remember { mutableStateOf(item.itemPrice.toString()) } var itemQuantity by remember { mutableStateOf(item.quantityInStock.toString()) } Log.d("EditScreen", "Editing values: itemName - $itemName, itemPrice - $itemPrice, itemQuantity - $itemQuantity") 我得到一个空项目:Editing values: itemName - , itemPrice - 0.0, itemQuantity - 0。知道为什么吗?
    【解决方案2】:

    您正在从视图模型中实时读取值,同时调用observeAsState。有修复就好了。你可以做类似的事情

        val receivedItem = itemViewModel.getItem(itemId!!.toInt()).observeAsState()
        val item = receivedItem.value ?: Item(0, "", 0.0, 0)
    
        var itemName by remember { mutableStateOf(item.itemName) }
        var itemPrice by remember { mutableStateOf(item.itemPrice.toString()) }
        var itemQuantity by remember { mutableStateOf(item.quantityInStock.toString) }
    
        //Why these?
        /*
        itemName = item.itemName
        itemPrice = item.itemPrice.toString()
        itemQuantity = item.quantityInStock.toString()
        */
        Column(
            modifier = Modifier
                .padding(horizontal = 32.dp, vertical = 16.dp)
                .fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            OutlinedTextField(
                value = itemName,
                onValueChange = { itemName = it },
                label = { Text("Item name") },
                modifier = Modifier
                    .padding(top = 16.dp)
                    .fillMaxWidth()
            )
    
            OutlinedTextField(
                value = itemPrice,
                onValueChange = { itemPrice = it },
                label = { Text("Item price") },
                modifier = Modifier
                    .padding(top = 16.dp)
                    .fillMaxWidth()
            )
    
            OutlinedTextField(
                value = itemQuantity,
                onValueChange = { itemQuantity = it },
                label = { Text("Item quantity") },
                modifier = Modifier
                    .padding(top = 16.dp)
                    .fillMaxWidth()
            )
            ...
    

    请注意,这不会修改视图模型中的值。正确的方法是在这个可组合项中创建一个名为 onItemChange 的参数,然后将其连接到视图模型的 onItemChange 方法,该方法将直接在视图模型中修改它。查看state hoisting

    【讨论】:

    • 感谢@MARSK!正如您所说,我最初已经这样做了,但是我对此有疑问。在这些行中:val receivedItem by itemViewModel.getItem(itemId!!.toInt()).observeAsState() val item = receivedItem ?: Item(0, "", 0.0, 0) Log.d("EditScreen", "Editing item: $item") 我得到的项目还好:D/EditScreen: Editing item: Item(id=2, itemName=Banana, itemPrice=0.3, quantityInStock=250)
    • 但是当我像你说的那样记住这些项目时:var itemName by remember { mutableStateOf(item.itemName) } var itemPrice by remember { mutableStateOf(item.itemPrice.toString()) } var itemQuantity by remember { mutableStateOf(item.quantityInStock.toString()) } Log.d("EditScreen", "Editing values: itemName - $itemName, itemPrice - $itemPrice, itemQuantity - $itemQuantity") 我得到一个空项目:Editing values: itemName - , itemPrice - 0.0, itemQuantity - 0。知道为什么吗?
    【解决方案3】:

    自己发现了错误并修复了问题。

    问题在于这一行:

    val receivedItem = itemViewModel.getItem(itemId!!.toInt()).observeAsState()
    

    receivedItem 最初从视图模型中获取一个空值,并在至少几次重组后得到更新,如下所示:

    2021-08-17 14:48:52.221 7985-7985/com.example.roomcrud D/EditScreen: Item id: 3
    2021-08-17 14:48:52.221 7985-7985/com.example.roomcrud D/EditScreen: receivedItem: Item(id=0, itemName=, itemPrice=0.0, quantityInStock=0)
    2021-08-17 14:48:52.221 7985-7985/com.example.roomcrud D/EditScreen: Editing values: itemName - ,  itemPrice - 0.0, itemQuantity - 0
    2021-08-17 14:48:52.255 7985-7985/com.example.roomcrud D/EditScreen: Item id: 3
    2021-08-17 14:48:52.255 7985-7985/com.example.roomcrud D/EditScreen: receivedItem: Item(id=3, itemName=Mango, itemPrice=2.2, quantityInStock=27)
    2021-08-17 14:48:52.255 7985-7985/com.example.roomcrud D/EditScreen: Editing values: itemName - ,  itemPrice - 0.0, itemQuantity - 0
    2021-08-17 14:48:52.271 7985-7985/com.example.roomcrud D/EditScreen: Item id: 3
    2021-08-17 14:48:52.271 7985-7985/com.example.roomcrud D/EditScreen: receivedItem: Item(id=3, itemName=Mango, itemPrice=2.2, quantityInStock=27)
    2021-08-17 14:48:52.271 7985-7985/com.example.roomcrud D/EditScreen: Editing values: itemName - Mango,  itemPrice - 2.2, itemQuantity - 27
    2021-08-17 14:48:52.553 7985-7985/com.example.roomcrud D/EditScreen: Item id: 3
    2021-08-17 14:48:52.554 7985-7985/com.example.roomcrud D/EditScreen: receivedItem: Item(id=3, itemName=Mango, itemPrice=2.2, quantityInStock=27)
    2021-08-17 14:48:52.554 7985-7985/com.example.roomcrud D/EditScreen: Editing values: itemName - Mango,  itemPrice - 2.2, itemQuantity - 27
    2021-08-17 14:48:52.568 7985-7985/com.example.roomcrud D/EditScreen: Item id: 3
    2021-08-17 14:48:52.569 7985-7985/com.example.roomcrud D/EditScreen: receivedItem: Item(id=3, itemName=Mango, itemPrice=2.2, quantityInStock=27)
    2021-08-17 14:48:52.569 7985-7985/com.example.roomcrud D/EditScreen: Editing values: itemName - Mango,  itemPrice - 2.2, itemQuantity - 27
    

    所以当我记住这些值时,它们是空的。如果我随后尝试使用局部变量更新值,那么每次重组都会覆盖这些值。所以我通过使用LaunchedEffect 最小化了覆盖,并且当数据库中的实际值到达并且不为空时只更新一次值。

    我现在可以编辑 TextFields 上的值了,因为它们不会在每次重组时被一遍又一遍地覆盖。

    这是最终生效的更新代码:

    @Composable
    fun EditScreen(
        itemId: String?,
        navController: NavController,
        itemViewModel: ItemViewModel,
        onSetAppTitle: (String) -> Unit,
        onShowFab: (Boolean) -> Unit
    ) {
        LaunchedEffect(Unit) {
            onSetAppTitle("Edit Item")
            onShowFab(false)
        }
    
        Log.d("EditScreen", "Item id: $itemId")
    
        val receivedItem: Item by itemViewModel.getItem(itemId!!.toInt())
            .observeAsState(Item(0, "", 0.0, 0))
    
        Log.d("EditScreen", "receivedItem: $receivedItem")
    
        var itemName by remember { mutableStateOf(receivedItem.itemName) }
        var itemPrice by remember { mutableStateOf(receivedItem.itemPrice.toString()) }
        var itemQuantity by remember { mutableStateOf(receivedItem.quantityInStock.toString()) }
    
        if (receivedItem.id != 0) {
            LaunchedEffect(Unit) {
                itemName = receivedItem.itemName
                itemPrice = receivedItem.itemPrice.toString()
                itemQuantity = receivedItem.quantityInStock.toString()
            }
        }
    
        Log.d(
            "EditScreen",
            "Editing values: itemName - $itemName,  itemPrice - $itemPrice, itemQuantity - $itemQuantity"
        )
    
        Column(
            modifier = Modifier
                .padding(horizontal = 32.dp, vertical = 16.dp)
                .fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            OutlinedTextField(
                value = itemName,
                onValueChange = { itemName = it },
                label = { Text("Item name") },
                modifier = Modifier
                    .padding(top = 16.dp)
                    .fillMaxWidth()
            )
    
            OutlinedTextField(
                value = itemPrice,
                onValueChange = { itemPrice = it },
                label = { Text("Item price") },
                modifier = Modifier
                    .padding(top = 16.dp)
                    .fillMaxWidth()
            )
    
            OutlinedTextField(
                value = itemQuantity,
                onValueChange = { itemQuantity = it },
                label = { Text("Item quantity") },
                modifier = Modifier
                    .padding(top = 16.dp)
                    .fillMaxWidth()
            )
    
            Button(
                onClick = {
                    if (itemViewModel.isItemValid(itemName, itemPrice, itemQuantity)) {
                        var updatedItem = receivedItem.copy(
                            itemName = itemName.trim(),
                            itemPrice = itemPrice.trim().toDouble(),
                            quantityInStock = itemQuantity.trim().toInt()
                        )
                        itemViewModel.updateItem(updatedItem)
                        navController.navigate("home") {
                            popUpTo("home") { inclusive = true }
                        }
                    }
                }, modifier = Modifier
                    .padding(top = 16.dp)
                    .fillMaxWidth()
            ) {
                Text(text = "Save")
            }
        }
    }
    

    希望它对未来的人有所帮助!谢谢大家的回答!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-22
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-29
      相关资源
      最近更新 更多