Каков самый простой способ установить порядок фокуса в Jetpack Compose?

У меня есть столбец текстовых полей, например:

      Column {
    TextField(
        value = ...,
        onValueChange = { ... },
        keyboardOptions = KeyboardOptions(imeAction = ImeAction.next),
    )
    TextField(
        value = ...,
        onValueChange = { ... },
        keyboardOptions = KeyboardOptions(imeAction = ImeAction.next),
    )
    .
    .
    .
}

Я хотел бы, чтобы каждое поле TextField перемещалось к следующему, когда пользователь нажимает Tab или следующую кнопку на клавиатуре. В настоящее время нажатие клавиши Tab вставляет вкладку в текстовое поле. Нажатие следующей кнопки ничего не делает. Я могу создать FocusRequester для каждого TextField и настроить keyboardActions onNext для запроса фокуса на следующем поле для каждого из них. Это немного утомительно и не касается поведения вкладок.

6 ответов

Недавно я нашел эту статью: https://medium.com/google-developer-experts/focus-in-jetpack-compose-6584252257fe

Он объясняет другой способ обработки фокуса, который немного проще.

      val focusManager = LocalFocusManager.current
TextField(
    modifier = Modifier
        .onKeyEvent {
            if (it.key.keyCode == Key.Tab.keyCode){
                focusManager.moveFocus(FocusDirection.Next)
                true
            } else {
                false
            }
        },
    value = text,
    onValueChange = { it -> text = it },
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
    keyboardActions = KeyboardActions(
        onNext = { focusManager.moveFocus(FocusDirection.Next) }
    )
)

Не уверен, что это проще, но вы можете создать FocusRequester объект для каждого поля и запросите фокус в нужном вам порядке.

      @Composable
fun FocusRequestScreen() {
    // Create FocusRequesters... (you can use createRefs function)
    val focusRequesters = List(3) { FocusRequester() }

    Column {
        TextFieldWithFocusRequesters(focusRequesters[0], focusRequesters[1])
        TextFieldWithFocusRequesters(focusRequesters[1], focusRequesters[2])
        TextFieldWithFocusRequesters(focusRequesters[2], focusRequesters[0])
    }
}

@Composable
private fun TextFieldWithFocusRequesters(
    focusRequester: FocusRequester,
    nextFocusRequester: FocusRequester
) {
    var state by rememberSaveable {
        mutableStateOf("Focus Transition Test")
    }
    TextField(
        value = state,
        onValueChange = { text -> state = text },
        // Here it is what you want...
        modifier = Modifier
            .focusOrder(focusRequester) {
                nextFocusRequester.requestFocus()
            }
        ,
        keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
    )
}

Я получаю этот код отсюда . Но это не решило проблему с вкладками ... :(

Я сейчас на compose_version = '1.0.2'.

Фокус не перемещается, когда вы нажимаете следующую кнопку, потому что, хотя действие клавиатуры по умолчанию заключается в перемещении фокуса на следующую , compose, похоже, не знает, какая из них должна быть следующей. Создание FocusRequester s для каждого элемента и установите порядок их фокусировки с помощью Modifier.focusOrder() {} может работать (кстати, не нужно устанавливать onNext чтобы запросить фокус, если вы выбираете этот способ), но поскольку ваши s находятся в том же Column, вы можете просто установить keyboardActionsчтобы сказать составить, переместите фокус на тот, который находится в нижнем направлении. Что-то вроде:

              Column {
            val focusManager = LocalFocusManager.current
            TextField(
                value = "", onValueChange = {},
                keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next ),
                keyboardActions = KeyboardActions(
                    onNext = { focusManager.moveFocus(FocusDirection.Down) }
                )
            )
            TextField(
                value = "", onValueChange = {},
                keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next ),
                keyboardActions = KeyboardActions(
                    onNext = { focusManager.moveFocus(FocusDirection.Down) }
                )
            )
            TextField(
                value = "", onValueChange = {},
                keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next ),
                keyboardActions = KeyboardActions(
                    onNext = { focusManager.moveFocus(FocusDirection.Down) }
                )
            )
        }

После этого должна сработать следующая кнопка на клавиатуре IME.

Для Tabключа, поскольку TextFieldне обрабатывает Tabключ автоматически, поэтому вы можете использовать focusManager внутри Modifier.onKeyEvent{} для перемещения фокуса так же, как в приведенном выше примере.

О порядке вы можете проверить ответ @nglauber .

Чтобы использовать клавишу Tab, вы можете использовать <strong> <tcode id="52899154"></tcode></strong>модификатор .

      TextField(
    modifier = Modifier
        .focusRequester(focusRequester)
        .onKeyEvent {
            if (it.key.keyCode == Key.Tab.keyCode){
                focusRequesterNext.requestFocus()
                true //true -> consumed
            } else false },
    value = text,
    onValueChange = { it -> text = it },
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
    keyboardActions = KeyboardActions(
        onNext = {focusRequesterNext.requestFocus()}
        )
)

Установите singleLine , попробуйте

      OutlinedTextField(
...
singleLine = true,
)

Простой пример

      @Composable
fun Test() {
    val focusManager = LocalFocusManager.current
    var text1 by remember {
        mutableStateOf("")
    }
    var text2 by remember {
        mutableStateOf("")
    }
    Column() {
        OutlinedTextField(value = text1, onValueChange = {
            text1 = it
        },
            keyboardOptions = KeyboardOptions(
                imeAction = ImeAction.Next,
                keyboardType = KeyboardType.Text
            ),
            keyboardActions = KeyboardActions(
                onNext ={
                    focusManager.moveFocus(FocusDirection.Down)
                }
            ),
            singleLine = true
            )
        OutlinedTextField(value = text2, onValueChange = {
            text2 = it
        },
            keyboardOptions = KeyboardOptions(
                imeAction = ImeAction.Next,
                keyboardType = KeyboardType.Text
            ),
            keyboardActions = KeyboardActions(
                onNext ={
                    focusManager.moveFocus(FocusDirection.Down)
                }
            ),
            singleLine = true
        )
    }
}

Кажется, у Google есть рекомендуемый подход, который еще не был предложен - https://developer.android.com/jetpack/compose/touch-input/focus/change-focus-traversal-order .

      val (first, second, third, fourth) = remember { FocusRequester.createRefs() }
      Column {
    Row {
        TextButton(
            {},
            Modifier
                .focusRequester(first)
                .focusProperties { next = second }
        ) {
            Text("First field")
        }
        TextButton(
            {},
            Modifier
                .focusRequester(third)
                .focusProperties { next = fourth }
        ) {
            Text("Third field")
        }
    }

    Row {
        TextButton(
            {},
            Modifier
                .focusRequester(second)
                .focusProperties { next = third }
        ) {
            Text("Second field")
        }
        TextButton(
            {},
            Modifier
                .focusRequester(fourth)
                .focusProperties { next = first }
        ) {
            Text("Fourth field")
        }
    }
}
Другие вопросы по тегам