Jetpack compose键盘操作监听器(setOnKeyListener)(用于短信的TextField)

6
我有五个文本字段。在第一个字段输入后,焦点会移动到下一个文本字段。 如果我删除了文本字段中的内容 - 焦点会移动到上一个文本字段。 所有与焦点相关的工作都通过onValueChanged部分完成。
但是,如果文本字段中的值为空(“”)-如果我按键盘上的退格键,onValueChanged中不会发生任何事情,因为字段中的值没有更改。我需要以某种方式将焦点设置在前一个文本字段上。

enter image description here

我该如何在撰写文本框中使用监听器来处理软键盘的返回按键?我尝试使用KeyboardActions。
keyboardActions = KeyboardActions(
                    onPrevious = {
                        //some work with foucs
                    },
                ),

但它没有起作用。
第二个问题: 如果文本框被点击(或获得焦点),如何将光标设置在文本末尾? 即使用户点击字符串的中间,我也需要将光标设置在末尾。

1
你可以使用Modifier.onKeyEvent。 - Gabriele Mariotti
我用我的解决方案创建了这个要点: https://gist.github.com/nglauber/312052d2989aff50b9331aa6e0f816f7 它部分工作,但由于某种原因,当焦点改变时键盘类型会快速更改...也许我们可以合并我们的解决方案 :) - nglauber
@nglauber 喜欢你的解决方案,它比我的好得多。 只需进行一些小改动:
  1. 在onValueChange中全部使用if (it.isDigitsOnly()) {code}以仅获取数字。
  2. 如果已经有新号码,请重写:
if (it.length > 1) { digits[i] = it.last().toString() return@TextField }
- Boris Sadakov
如果文本框中已经有内容,则将焦点移至下一个(在.onKeyEvent中)。 else if (digits[i] != "") { focus[i+1].requestFocus() } - Boris Sadakov
2个回答

2
@ExperimentalComposeUiApi
@Composable
fun EnterOtpView() {
    val scrollState = rememberScrollState()
    val focusManager = LocalFocusManager.current
    val digits = remember {
        mutableStateListOf(
            *((0 until 4).map { "" }.toTypedArray())
        )
    }
    val focusRequesters: List<FocusRequester> = remember {
        (0 until 4).map { FocusRequester() }
    }
    Column(
        Modifier.padding(horizontal = 70.dp),  
        verticalArrangement = Arrangement.Top,
        horizontalAlignment = Alignment.CenterHorizontally,

        ) {
        Text(
            text = "123 456 8550", style = MaterialTheme.typography.h3, color = Purple,
            modifier = Modifier.padding(top = 20.dp, bottom = 30.dp)
        )
        Row(
            modifier = Modifier
                .fillMaxWidth(1f)
                .wrapContentHeight(),
            Arrangement.SpaceBetween,
            Alignment.CenterVertically
        ) {
            (0 until 4).forEach { index ->
                TextField(
                    modifier = Modifier
                        .weight(0.2f)
                        .padding(end = 4.dp)
                        .onKeyEvent {
                            if (it.nativeKeyEvent.keyCode == 67) {
                                if (digits[index].isEmpty()) {
                                    focusManager.moveFocus(FocusDirection.Left)
                                }
                                digits[index] = ""
                            }
                            true
                        }
                        .padding(vertical = 2.dp)
                        .focusOrder(focusRequesters[index])
                        .focusRequester(focusRequesters[index]),
                    colors = TextFieldDefaults.textFieldColors(
                        backgroundColor = Color.Transparent
                    ),
                    singleLine = true,
                    textStyle = MaterialTheme.typography.body1.copy(textAlign = TextAlign.Center),
                    value = digits[index],
                    onValueChange = {
                        if (digits[index].isEmpty() && it.isDigitsOnly()) {
                            digits[index] = it
                            focusManager.moveFocus(FocusDirection.Right)
                        }
                    },
                    keyboardOptions = KeyboardOptions(
                        imeAction = ImeAction.Next,
                        keyboardType = KeyboardType.Number
                    ),
                    keyboardActions = KeyboardActions(
                        onNext = {
                            focusManager.moveFocus(FocusDirection.Right)
                        }
                    )
                )
            }
        }
}}

使用nativeKeyEvent和focusManager对我很有效。


1

使用@nglauber的解决方案,为输入短信添加文本字段(目前仅限此):

@Composable
fun SMSTextFields(
    modifier: Modifier,
    smsCodeLength: Int = 5,
    whenFull: (smsCode: String) -> Unit
) {
    val enteredNumbers = remember {
        mutableStateListOf(
            *((0 until smsCodeLength).map { "" }.toTypedArray())
        )
    }
    val focusRequesters: List<FocusRequester> = remember {
        (0 until smsCodeLength).map { FocusRequester() }
    }
    Row(modifier = modifier.padding(start = 60.dp, end = 60.dp)) {
        (0 until smsCodeLength).forEach { index ->
            TextField(
                modifier = Modifier
                    .weight(1f)
                    .size(120.dp, 80.dp)
                    .onKeyEvent { event ->
                        val cellValue = enteredNumbers[index]
                        if (event.type == KeyEventType.KeyUp) {
                            if (event.key == Key.Backspace && cellValue == "") {
                                focusRequesters
                                    .getOrNull(index - 1)
                                    ?.requestFocus()
                                enteredNumbers[index - 1] = ""
                            } else if (cellValue != "") {
                                focusRequesters
                                    .getOrNull(index + 1)
                                    ?.requestFocus()
                            }
                        }
                        false
                    }
                    .padding(vertical = 2.dp)
                    .focusOrder(focusRequesters[index])
                    .focusRequester(focusRequesters[index]),
                colors = TextFieldDefaults.textFieldColors(
                    backgroundColor = whiteBackground,
                    unfocusedIndicatorColor = greyColor,
                    focusedIndicatorColor = signUpColorButton,
                    cursorColor = greyColor,
                    textColor = greyColor
                ),
                textStyle = smsCodeEnterStyle,
                singleLine = true,
                value = enteredNumbers[index],
                onValueChange = { value: String ->
                    if (value.isDigitsOnly()) {
                        if (value.length > 1) {
                            enteredNumbers[index]  = value.last().toString()
                            return@TextField
                        }
                        if (focusRequesters[index].freeFocus()) {
                            enteredNumbers[index] = value
                            if (enteredNumbers[index].isBlank() && index > 0 && index <5) {
                                focusRequesters[index - 1].requestFocus()
                            } else if (index < smsCodeLength - 1) {
                                focusRequesters[index + 1].requestFocus()
                            }
                            else if (enteredNumbers.size == 5){
                                whenFull(enteredNumbers.joinToString(separator = ""))
                            }
                        }
                    }
                },
                keyboardOptions = KeyboardOptions.Default.copy(
                    keyboardType = KeyboardType.Number,
                    imeAction = ImeAction.Next
                ),
            )
            Spacer(modifier = Modifier.width(4.dp))
        }
    }
}

1
使用这两种解决方案后,当焦点改变时,我仍然遇到键盘问题... 我已经为此打开了一个工单... https://issuetracker.google.com/issues/187746439 - nglauber
@nglauber,是的,我有同样的问题。 - Boris Sadakov
1
我已经创建了一篇文章,解释了一个可能的解决方法,你也可以在这里使用相同的方法,希望能有所帮助。https://chetangupta.net/keyboard-switch-bug/ - Chetan Gupta

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