简短:在我的文本字段中,为什么在我的协程完成后“查询”值没有被清除?
在我的文本字段可组合项中,我正在测试通过挂起协程传递的“查询”值,以便发生一些突变。例如,这是在预先输入搜索的情况下,其中可能会调用 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 值为空,但却显示了相同的值。
虽然作业顺利完成,但该值并未被清除。
该问题源于 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()
}
)
)