首页 > 解决方案 > 如何在可组合功能中一次只打开一个编辑表单

问题描述

我在 LazyColumn 中显示了一个包含一个单词的行列表。单击该行时,将打开一个编辑表单。数据来自房间数据库。

由于该行位于单独的可组合函数上,因此我可以一起打开许多不同的编辑表单(每行一个)。但我想一次只在整个列表中显示一个编辑表单。如果我单击一行打开编辑表单,则应关闭其他行上的其余打开表单。我怎样才能做到这一点?

这是代码:

    val words: List<Word> by wordViewModel.allWords.observeAsState(listOf())

    var newWord by remember { mutableStateOf("") }
    val context = LocalContext.current
    val keyboardController = LocalSoftwareKeyboardController.current

        LazyColumn(
            modifier = Modifier
                .weight(1f)
                .padding(vertical = 16.dp),
            verticalArrangement = Arrangement.spacedBy(4.dp)
        ) {
            items(words) { word ->
                WordItemLayout(
                    word = word,
                    onSaveUpdatedWord = { onUpdateWord(it) },
                    onTrashClicked = { onDeleteWord(it) }
                )
            }
        }
@Composable
fun WordItemLayout(word: Word, onSaveUpdatedWord: (Word) -> Unit, onTrashClicked: (Word) -> Unit) {
    var showEditForm by remember { mutableStateOf(false) }
    var editedWord by remember { mutableStateOf(word.word) }
    val context = LocalContext.current

    Column {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .background(MaterialTheme.colors.primaryVariant)
                .padding(vertical = 12.dp, horizontal = 24.dp)
                .clickable {
                    showEditForm = !showEditForm
                    editedWord = word.word
                },
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Image(painter = painterResource(R.drawable.ic_star), contentDescription = null)
            Text(
                text = word.word,
                color = Color.White,
                fontSize = 20.sp,
                modifier = Modifier
                    .padding(start = 16.dp)
                    .weight(1f)
            )
            // Delete Button
            IconButton(
                onClick = {
                    showEditForm = false
                    onTrashClicked(word)
                    Toast.makeText(context, "Word deleted", Toast.LENGTH_SHORT).show()
                },
                modifier = Modifier.size(12.dp)
            ) {
                Icon(
                    imageVector = Icons.Filled.Delete,
                    contentDescription = "Delete Word",
                    tint = Color.White
                )
            }
        }

        // word edit form
        if (showEditForm) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                verticalAlignment = Alignment.Bottom
            ) {
                TextField(
                    value = editedWord,
                    onValueChange = { editedWord = it },
                    modifier = Modifier.weight(1f),
                    colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White) // TextField Background Color
                )
                // Update Button
                Button(
                    onClick = {
                        val updatedWord: Word = word

                        if (updatedWord.word != editedWord.trim()) {
                            updatedWord.word = editedWord.trim()
                            onSaveUpdatedWord(updatedWord)
                            Toast.makeText(context, "Word updated", Toast.LENGTH_SHORT).show()
                        }

                        showEditForm = false
                    },
                    modifier = Modifier.padding(start = 8.dp)
                ) {
                    Icon(imageVector = Icons.Filled.Done, contentDescription = "Update Word")
                }
            }
        }
    }
}

谢谢你的帮助!

标签: androidkotlinandroid-jetpack-compose

解决方案


一种方法:在您的视图模型中,声明一个openRowIndex状态(这将存储打开行的索引,例如,您可以将其初始化为 -1)。定义一个可以改变这个状态的方法,例如updateOpenRowIndex

我不确定您在视图模型中使用的是哪种状态持有者。我将StateFlow用于这个答案。在您的视图模型中声明新的状态和方法:

private val _openRowIndex = MutableStateFlow(-1)
val openRowIndex: StateFlow<Int> = _openRowIndex

fun updateOpenRowIndex(updatedIndex: Int) {
   _openRowIndex.value = updatedIndex
}

对于可兼容的每一行,将index其传入LazyColumn. itemsIndexed您可以使用该方法获取索引。还要收集你的openRowIndex,并将其传递给可组合的。还传入更新打开行索引的方法:

itemsIndexed(words) { index, word ->
    //get the current opened row state and collect it (might look different for you if you are not using StateFlow):
    val openRowIndex = wordViewModel.openRowIndex.collectAsState()
    WordItemLayout(
        word = word,
        onSaveUpdatedWord = { onUpdateWord(it) },
        onTrashClicked = { onDeleteWord(it) },
        index = index, //new parameter!
        openRowIndex = openRowIndex.value //new parameter!
        onUpdateOpenedRow = wordViewModel::updateOpenRowIndex //new parameter!
    )

}

现在,在可组合的行中,只需检查indexand是否openRowIndex匹配,仅当它们匹配时才显示打开的行。现在更新打开的行:使Row可点击,并在点击时使用视图模型updateOpenRowIndex方法将状态更新为index. 当状态随着新打开的行发生变化时,Compose 将处理其余部分并重新组合!

fun WordItemLayout(
   word: Word,
   onSaveUpdatedWord: (Word) -> Unit,
   onTrashClicked: (Word), -> Unit,
   index: Int, //new parameters
   openRowIndex: Int,
   onUpdateOpenedRow: (Int) -> Unit
) {
   if(index == openRowIndex) {
      //display this row as opened
   } else {
      //display this row as closed
   }
}

正如我所说,使该行可点击并调用更新函数:

Row(
   modifier = Modifier.clickable {
      onUpdateOpenedRow(index)
      //additional instructions for what to happen when row is clicked...
   }
   //additional row parameters...
)

推荐阅读