如何关闭 Jetpack Compose TextField 中的虚拟键盘?

116
我正在使用Jetpack Compose的TextField,我希望在用户按下操作按钮(imeActionPerformed参数)时关闭虚拟键盘。
val text = +state { "" }
TextField(
    value = text.value,
    keyboardType = KeyboardType.Text,
    imeAction = ImeAction.Done,
    onImeActionPerformed = { 
        // TODO Close the virtual keyboard here <<<
    }
    onValueChange = { s -> text.value = s }
)
8个回答

247
您可以使用LocalSoftwareKeyboardController 类来控制当前的软键盘,然后使用hide 方法:
var text by remember { mutableStateOf(TextFieldValue("Text")) }
val keyboardController = LocalSoftwareKeyboardController.current

TextField(
        value = text,
        onValueChange = {
            text = it
        },
        label = { Text("Label") },
        keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
        keyboardActions = KeyboardActions(
                onDone = {keyboardController?.hide()})
)

这个解决方案关闭键盘,但不会从当前 TextField 中移除焦点。

仅为了强调与以下差异:

val focusManager = LocalFocusManager.current
focusManager.clearFocus()

这段代码关闭键盘,同时将焦点从TextField中移除


73
这是每个安卓开发者的梦想。 - Andrew Chelix
3
这种方法与focusManager方法(https://dev59.com/PFIH5IYBdhLWcg3wkvtX#66259111)有何区别?应该在什么情况下使用它们? - stkent
2
@stkent 这个解决方案只是隐藏了键盘。另一个解决方案还会将焦点从当前的文本框中移除(光标仍然显示,可能仍然被突出显示等)。 - m.reiter
1
在选择两种解决方案时,请注意UX连续性:默认行为是向后滑动一次隐藏键盘,第二次滑动清除焦点。 - m.reiter
1
@stkent,有关详细信息,请参见我下面的答案 https://dev59.com/PFIH5IYBdhLWcg3wkvtX#68650009 - m.reiter
显示剩余3条评论

107
从compose 1.0.0-alpha12开始(并且在compose 1.5.3仍然有效),onImeActionPerformed已被弃用,建议使用keyboardActionskeyboardOptions的组合方法:
    val focusManager = LocalFocusManager.current

    OutlinedTextField(
        value = ...,
        onValueChange = ...,
        label = ...,
        keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
        keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done, keyboardType = KeyboardType.Password),
    )

focusManager.clearFocus()会负责隐藏软键盘。


1
这在“AlertDialog”中无法工作。我尝试过的所有方法,包括“SoftwareKeyboardController”、“FocusManager”和“FocusRequester”,都无法在对话框中起作用。有什么想法吗? - shelll
2
@shelll 尝试在对话框内初始化FocusManager。在我的情况下,它起作用了。 - Drogheda
@Drogheda 是的,就是这个!谢谢!也许可以将其作为单独的答案发布。 - shelll
@shelll 你在对话中找到解决办法了吗?我也在对话中遇到了同样的问题。 - fargath
@shelll 你在对话中找到解决方案了吗?我也在对话中遇到了同样的问题。 - undefined
@fargath 是的,上面那条评论,来自Drogheda,解决了问题! - shelll

22

1.0.0 版本中,您可以使用 SoftwareKeyboardControllerFocusManager 来完成此操作。

本答案着重介绍它们之间的区别。


设置:

var text by remember { mutableStateOf("")}

TextField(
    value = text,
    onValueChange = { text = it },
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
    keyboardActions = KeyboardActions(onDone = { /* TODO */ }),
)

设置


软件键盘控制器:

基于@Gabriele Mariottis的回答。

val keyboardController = LocalSoftwareKeyboardController.current

// TODO =
keyboardController?.hide()

这只会关闭键盘,但不会清除任何已聚焦的TextField的焦点(请注意光标和粗下划线)。

使用键盘控制器


FocusManager:

根据@azizbekians的回答。

val focusManager = LocalFocusManager.current

// TODO =
focusManager.clearFocus()

使用焦点管理器

这将关闭键盘并清除TextField的焦点。


8

点击按钮时隐藏键盘

除了Gabriele Mariotti的解决方案,如果您想有条件地隐藏键盘,比如在按钮点击后,可以使用以下代码:

keyboardController?.hide()

例如,在单击“添加”按钮后隐藏键盘:
var newWord by remember { mutableStateOf("") }
val keyboardController = LocalSoftwareKeyboardController.current

// Setup the text field with keyboard as provided by Gabriele Mariotti

...

Button(
        modifier = Modifier
                .height(56.dp),
        onClick = {
                if (!newWord.trim().isNullOrEmpty()) {
                        wordViewModel.onAddWord(newWord.trim())
                        newWord = ""
                        keyboardController?.hide()
                }
        ...

当我们点击按钮时,如何显示键盘?(在这种情况下,keyboardController?.show()不起作用) - Hasan Cihad Altay

4

alpha-12发布后编辑: 请参考@azizbekian的回答。

Pre-alpha-12回复

我在这里找到了解决方案:)

fun hideKeyboard(activity: Activity) {
    val imm: InputMethodManager = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    var view = activity.currentFocus
    if (view == null) {
        view = View(activity)
    }
    imm.hideSoftInputFromWindow(view.windowToken, 0)
}

我只需要从我的组件中调用上面的函数:
// getting the context
val context = +ambient(ContextAmbient)

// textfield state
val text = +state { "" }

TextField(
    value = text.value,
    keyboardType = KeyboardType.Text,
    imeAction = ImeAction.Done,
    onImeActionPerformed = { 
        if (imeAction == ImeAction.Done) {
            hideKeyboard(context as Activity)
        }
    }
    onValueChange = { s -> text.value = s }
)

4
有两种情况
情况1 - AlertDialog中的TextField
确保在Dialog内容范围内初始化键盘控制器或焦点管理器
(Dialog有自己的键盘控制器)
Dialog(
    onDismissRequest = {
       // on dismiss
    }
) {
   // initialise the keyboard controller and focus Manager inside the content scope
   val keyboardController = LocalSoftwareKeyboardController.current
   val focusManager = LocalFocusManager.current
}


隐藏键盘
keyboardController?.hide()
focusManager.clear()

边缘情况 - 关闭对话框并隐藏键盘
可能会出现键盘闪烁(键盘快速隐藏和显示)问题,当同时关闭对话框和键盘时。
尝试先隐藏键盘,然后延迟触发关闭对话框事件。
Dialog() {
    val keyboardController = LocalSoftwareKeyboardController.current
    val focusManager = LocalFocusManager.current

    Button(onClick = {
       keyboardController?.hide()
       focusManager.clear()

       // notify the view model to dismiss the dialog

       viewModel.onNegativeButtonClicked()
    })
}

在ViewModel内部
ViewModel {
    
  fun onNegativeButtonClicked() {

     // trigger dismissDialog event with delay
  }

}

场景2 - 仅文本框(无对话框)
val keyboardController = LocalSoftwareKeyboardController.current
var text by rememberSaveable { mutableStateOf("") }
TextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Label") },
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
    keyboardActions = KeyboardActions(
        onDone = {
            keyboardController?.hide()
            // do something here
        }
    )
)

或者你可以在点击事件中使用keyboardController和focusManager来隐藏键盘。
Button(onClick = {
    keyboardController?.hide()
})

1
请确保在对话框范围内初始化键盘控制器或焦点管理器(对话框有自己的键盘控制器)。谢谢,我在这上面浪费了一个小时。 - stijndcl

1
我在CoreTextField中找到了一种关闭它的方法,使用TextInputService来控制开关。
val focus = LocalTextInputService.current
var text by remember{ mutableStateOf("")}
TextField(
    value = text,
    onValueChange = { text = it },
    keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done, keyboardType = KeyboardType.Text),
    keyboardActions = KeyboardActions(onDone = { focus?.hideSoftwareKeyboard() }),
    singleLine = true
)

请参见“完全基于代码的答案解释”。虽然这可能在技术上是正确的,但它并没有解释为什么它可以解决问题或应该被选为答案。我们应该在帮助解决问题的同时进行教育。 - the Tin Man
即使在“AlertDialog”中也无法正常工作。有什么想法吗? - shelll

0

implementation 'androidx.compose.material3:material3:1.0.0-alpha02'

隐藏键盘的文本框

@OptIn(ExperimentalComposeUiApi::class)
    @Composable
    fun TextFieldWithHideKeyboardOnImeAction() {
        val keyboardController = LocalSoftwareKeyboardController.current
        var text by rememberSaveable { mutableStateOf("") }
        TextField(
            value = text,
            onValueChange = { text = it },
            label = { Text("Label") },
            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
            keyboardActions = KeyboardActions(
                onDone = {
                    keyboardController?.hide()
                    // do something here
                }
            )
        )
    }

Text Field With Hide Keyboard On Ime Action


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接