Jetpack Compose 框架 - Android - Kotlin - 在文本字段中,为什么在我的挂起协程完成后“查询”值没有更改?

问题描述 投票:0回答:1

简短:在我的文本字段中,为什么在我的协程完成后“查询”值没有被清除?

在我的文本字段可组合项中,我正在测试通过挂起协程传递的“查询”值,以便发生一些突变。例如,这是在预先输入搜索的情况下,其中可能会调用 api 端点 (Dispatchers.IO),并且会反序列化字符串值。

为了清除文本字段,一旦用户点击“发送”,查询将重置为空白。虽然可以在协程范围内清除它,这工作得很好,但我想知道为什么即使作业已经明确完成,该值也没有在协程之外清除。正在完成的作业显示在简单的文本值更改中。

代码:

@Composable
fun Search(){
    val keyboard = LocalSoftwareKeyboardController.current
    val focusManager = LocalFocusManager.current
    val focusRequester = remember { FocusRequester() }
    val scope = rememberCoroutineScope()
    var changedValue by remember { mutableStateOf("The value is untouched") }
    var query by remember { mutableStateOf("") }
    var queryThread: Job by remember { mutableStateOf(Job()) }
    Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center){
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically){
            TextField(maxLines = 1,
                value = query,
                onValueChange = { query = it },
                modifier = Modifier.width(300.dp)
                    .focusRequester(focusRequester),
                enabled = true,
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text,
                    capitalization = KeyboardCapitalization.Words,
                    imeAction = ImeAction.Done),
                keyboardActions = KeyboardActions(
                    onDone = {
// This is the co-routine, where a value might be mutated

                        queryThread = scope.launch {
                            changedValue = changeValue(query)

                        }
                        keyboard!!.hide()
                        focusManager.clearFocus()
// This would reset the "query" value once the coroutine is completed

                        if (queryThread.isCompleted){
                            query = ""
                        }
                    }
                )
            )
        }
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically) {
            Text(changedValue, textAlign = TextAlign.Center)
        }
        HorizontalDivider(thickness = 10.dp, modifier = Modifier.width(10.dp),
            color = Color.Transparent)
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically) {
            val completed = if (queryThread.isCompleted) "Thread has finished running" else "Thread is still running"
            Text(completed, textAlign = TextAlign.Center)
        }
    }
}


suspend fun changeValue(query: String): String{
    return withContext(Dispatchers.IO) {
        val result = "The value $query passed through a suspend co-routine."
        return@withContext result
    }
}

我尝试过的和我期待的:

我尝试在 Job() 完成后清除查询值,并使用简单的条件语句进行检查。

我预计 TextField 值为空,但却显示了相同的值。

虽然作业顺利完成,但该值并未被清除。

kotlin android-jetpack-compose kotlin-coroutines suspend
1个回答
0
投票

该问题源于 Jetpack Compose 中协程处理 UI 更新的方式。这就是为什么在协程完成后查询值没有重置的原因:

问题:

您正在 onDone 回调中检查协程的完成状态 (queryThread.isCompleted)。但是,Compose 中的 UI 更新不能直接从协程内发生。因此,即使协程完成且 if 语句计算结果为 true,UI 也不会立即更新。

您可以通过使用带有完成回调的启动来执行 UI 更新来解决此问题。协程完成后,此回调将在主线程上执行,让您可以安全地更新 UI。 检查此代码:

TextField(
...
keyboardActions = KeyboardActions(
    onDone = {
        queryThread = scope.launch {
            changedValue = changeValue(query)
        }.apply {
            invokeOnCompletion {
                query = "" // Clear query after completion
            }
        }
        keyboard!!.hide()
        focusManager.clearFocus()
    }
)
)
© www.soinside.com 2019 - 2024. All rights reserved.