Jetpack Compose:设置ImeAction无法关闭或更改键盘的焦点

41
我正在使用Jetpack compose 1.0.0-alpha07。我制作了一个包含两个TextField的登录界面,并使用其他组成部分进行自定义。
然而,在keyboardOptions中设置ImeAction似乎不起作用。例如,ImeAction.Next不能将焦点移动到下一个TextField。我认为我应该做一些事情来使其成为可能,但是没有任何文档或文章甚至简要介绍过ImeOptions。以下是我屏幕的代码:
Login组件:
EmailEdit(onChange = { email.value = it })
PasswordEdit(onChange = { password.value = it })

EmailEdit:

@Composable
fun EmailEdit(onChange: (String) -> Unit) {
    val t = remember { mutableStateOf("") }
    TextField(
        value = t.value,
        onValueChange = { value ->
            t.value = value
            onChange(value)
        },
        leadingIcon = { Icon(asset = Icons.Default.Email) },
        label = { Text(text = "Email") },
        maxLines = 1,
        keyboardOptions = KeyboardOptions(
            imeAction = ImeAction.Next, // ** Go to next **
            keyboardType = KeyboardType.Email
        ),
        visualTransformation = VisualTransformation.None
    )
}      errorHint = "Not a valid email"
    )
}

PassEdit:

@Composable
fun PasswordEdit(onChange: (String) -> Unit) {
    val t = remember { mutableStateOf("") }
    TextField(
        value = t.value,
        onValueChange = { value ->
            t.value = value
            onChange(value)
        },
        leadingIcon = { Icon(asset = Icons.Default.Security) },
        label = { Text(text = "Password") },
        maxLines = 1,
        keyboardOptions = KeyboardOptions(
            imeAction = ImeAction.Done, // ** Done. Close the keyboard **
            keyboardType = KeyboardType.Text
        ),
        visualTransformation = PasswordVisualTransformation()
    )
}

要执行 DoneNext,我需要添加什么代码?

4个回答

63

您可以使用:

  • keyboardOptions:包含配置信息,如KeyboardTypeImeAction的软件键盘选项
  • keyboardActions:当输入服务发出IME操作时,将调用相应的回调函数

对于Done

您可以使用LocalSoftwareKeyboardController与键盘进行交互。

keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
    onDone = {keyboardController?.hide()}
)

下一个 (Next):

keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
    onNext = { focusRequester.requestFocus() }
)

类似这样的:

val (focusRequester) = FocusRequester.createRefs()
val keyboardController = LocalSoftwareKeyboardController.current

TextField(
    value = text,
    onValueChange = {
        text = it
    },
    label = { Text("Label") },
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
    keyboardActions = KeyboardActions(
        onNext = { focusRequester.requestFocus() } 
    )
)

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

5
用这种方式请求焦点时,我发现出现异常: java.lang.IllegalStateException: FocusRequester未初始化。其中一个原因是您在构建期间请求了焦点更改。不应该在构建期间创建焦点请求器,而应该响应某些事件来创建它们。 - Yupi
2
添加 Modifier.focusRequester(focusRequester) 可解决 IllegalStateException。 - bluevoid
2
在横屏模式下按下“下一步”键时,键盘会关闭,而在竖屏模式下正常工作。 - Jatin Sachdeva
1
LocalSoftwareKeyboardController仍处于实验阶段,我们是否应该在生产中使用它? - Amit
3
使用Compose 1.1.1似乎不需要keyboardActions和手动焦点管理。 KeyboardOptions(imeAction = ImeAction.Next)就足以获得正确的行为。 - gmk57

27
您可以使用LocalFocusManager
val localFocusManager = LocalFocusManager.current

在您字段的父合成中。

将焦点移动到下一个字段:

localFocusManager.moveFocus(FocusDirection.Down)

在KeyboardActions的onNext方法中,可以将焦点移动到特定方向,例如左、右、上和下。

要清除焦点:

localFocusManager.clearFocus()
< p >在 KeyboardActionsonDone 中清除焦点。

电子邮件字段:

OutlinedTextField(
            value = userId,
            onValueChange = { userId = it },
            label = { Text("Email") },
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
            leadingIcon = {
                Icon(
                    painter = painterResource(id = R.drawable.ic_account),
                    contentDescription = "ID"
                )
            },
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color.Gray,
                unfocusedBorderColor = Color.LightGray,
                focusedLabelColor = Color(0xffcc0000)
            ),
            keyboardOptions =
            KeyboardOptions(
                keyboardType = KeyboardType.Text,
                imeAction = ImeAction.Next
            ),
            keyboardActions = KeyboardActions(onNext = {
                localFocusManager.moveFocus(FocusDirection.Down)
            })
        )

密码字段:

OutlinedTextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("Password") },
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
            leadingIcon = {
                Icon(
                    painter = painterResource(id = R.drawable.ic_password),
                    contentDescription = "Password"
                )
            },
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color.Gray,
                unfocusedBorderColor = Color.LightGray,
                focusedLabelColor = Color(0xffcc0000)
            ),
            keyboardOptions =
            KeyboardOptions(
                keyboardType = KeyboardType.Password,
                imeAction = ImeAction.Done
            ),
            keyboardActions = KeyboardActions(onDone = {
                localFocusManager.clearFocus()
            })

        )

尝试使用版本 1.0.1


3
这个方法是有效的,但是当一个字段不可见时,比如在键盘后面时,它就无法起作用了。 - Bruce Wayne

15

使用onImeActionPerformed参数。

对于Done

TextField(
    onImeActionPerformed = { _, controller ->
        controller?.hideSoftwareKeyboard()
    }
)

下一个(Next):

val focusRequester = remember { FocusRequester() }
TextField(
    onImeActionPerformed = { _, _ ->
        focusRequester.requestFocus()
    }
)
TextField(
    modifier = Mofifier.focusRequester(focusRequester)
)

这是一个工作示例:

val focusRequester = remember { FocusRequester() }
val email = remember { mutableStateOf("") }
TextField(
    value = email.value,
    onValueChange = { email.value = it },
    imeAction = ImeAction.Next,
   onImeActionPerformed = { _, _ -> focusRequester.requestFocus() }
)
val password = remember { mutableStateOf("") }
TextField(
    value = password.value,
    onValueChange = { password.value = it },
    imeAction = ImeAction.Done,
    onImeActionPerformed = { _, controller -> controller?.hideSoftwareKeyboard() },
    modifier = Modifier.focusRequester(focusRequester)
)

文档:


2
如果我们有超过两个文本字段,那么该如何逐步聚焦下一步呢? - Rustam Samandarov
1
我认为对于每个文本字段,我们都需要创建新的FocusRequester。 - Rustam Samandarov

7

在这个示例中,使用Compose需要创建一组对屏幕上每个可聚焦文本字段的引用(textField1、textField2),并获取一个指向keyboardController的引用。

然后,您可以向键盘选项添加一种类型的操作,以指示键盘显示时操作按钮的外观。

在keyboardActions参数中,您可以在指定的IME操作上调用一个函数-在这种情况下,我已经说明了当按下键盘动作按钮时,我希望textField2获得焦点。您可以将引用分配给TextField中的Modifier.focusRequester参数。

最后,为了使您的第一个TextField在屏幕出现时获得焦点,您需要调用DisposableEffect函数,并指定您希望在屏幕首次显示时,textField1获得焦点。

val (textField1, textField2) = remember { FocusRequester.createRefs() }
val keyboardController = LocalSoftwareKeyboardController.current

TextField(
    modifier = Modifier.focusRequester(textField1),
    keyboardOptions  = KeyboardOptions(imeAction = ImeAction.Next),
    keyboardActions = KeyboardActions( onNext = {textField2.requestFocus()}
   ),
)


 TextField(
    modifier = Modifier.focusRequester(textField2),
    keyboardOptions  = KeyboardOptions(imeAction = ImeAction.Done),
    keyboardActions = KeyboardActions( onDone = { 
    keyboardController?.hide()}
   ),
)

DisposableEffect(Unit) {
    textField1.requestFocus()
    onDispose { }
}

1
这似乎是最好的方法,可以处理隐藏在键盘后面的字段,并且可以控制焦点移动到哪里。 - Bruce Wayne

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