Jetpack Compose,TextField中的自定义光标位置

14
当一个TextField获得焦点时,我如何将光标设置在随机位置上?在经典的Android视图系统中,相当于editText.setSelection(position)。以下是我用来使编辑文本在加入屏幕时自动接收焦点的代码。我想能够移动默认位置0处的光标。
val (getText, setText) = remember { mutableStateOf("hello") }
AutofocusEditText(
    text = getText,
    setText = setText
)
    
...

@Composable
private fun AutofocusEditText(
    text: String,
    setText : (String) -> Unit
) {
    val focusState = remember { mutableStateOf(FocusState.Inactive) }
    val focusRequester = FocusRequester()
    val focusModifier = Modifier.focus()
    Row(
        modifier = Modifier.focusObserver { newFocusValue -> focusState.value = newFocusValue }
    ) {
        val focusRequesterModifier =
            Modifier.focusRequester(focusRequester)

        TextField(
            value = text,
            modifier = focusModifier.then(focusRequesterModifier),
            backgroundColor = Color.Transparent,
            onValueChange = setText,
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Done
            ),
            onImeActionPerformed = { action, softKeyboardController ->
                if (action == ImeAction.Done) {
                    softKeyboardController?.hideSoftwareKeyboard()
                }
            }
        )
    }
    onActive {
        focusRequester.requestFocus()
    }
}
3个回答

19
您需要使用TextFieldValue版本的TextField。
@Composable
fun TextField(
    value: TextFieldValue,
    onValueChange: (TextFieldValue) -> Unit,
    /* ... */) {/* Impl */}

代码示例: 编辑:Compose版本1.1.1(13.04.22)

enum class CursorSelectionBehaviour {
    START, END, SELECT_ALL
}

@Composable
fun AutofocusTextFieldExample(
    initValue: String,
    behaviour: CursorSelectionBehaviour = CursorSelectionBehaviour.END
) {
    val direction = LocalLayoutDirection.current
    var tfv by remember {
        val selection = when (behaviour) {
            CursorSelectionBehaviour.START -> {
                if (direction == Ltr) TextRange.Zero else TextRange(initValue.length)
            }
            CursorSelectionBehaviour.END -> {
                if (direction == Ltr) TextRange(initValue.length) else TextRange.Zero
            }
            CursorSelectionBehaviour.SELECT_ALL -> TextRange(0, initValue.length)
        }
        val textFieldValue = TextFieldValue(text = initValue, selection = selection)
        mutableStateOf(textFieldValue)
    }
    val focusRequester = remember { FocusRequester.Default }
    TextField(
        modifier = Modifier.focusRequester(focusRequester),
        value = tfv,
        onValueChange = { tfv = it }
    )
    LaunchedEffect(Unit) {
        focusRequester.requestFocus()
    }
}

OLD (04.01.21):

    @Composable
    fun AutoFocusingText() {
        val textState = remember { mutableStateOf(TextFieldValue()) }
        val focusState = remember { mutableStateOf(FocusState.Inactive) }
        val focusRequester = FocusRequester()
        val focusModifier = Modifier.focus()
        Row(
            modifier = Modifier.focusObserver { focusState.value = it }
        ) {
            val focusRequesterModifier = Modifier.focusRequester(focusRequester)
            TextField(
                modifier = focusModifier.then(focusRequesterModifier),
                value = textState.value,
                onValueChange = { value: TextFieldValue ->
                    textState.value = value
                }
            )
        }
        onActive {
            focusRequester.requestFocus()
        }
    }

如果你的初始值是非空字符串,你必须手动更改选择。用以下内容替换空的TextFieldValue:TextFieldValue(text = value, selection = TextRange(value.length, value.length))

如果要像代码中那样提取值,则需要将当前选择作为参数添加或与TextFieldValue结合提取。否则,如果用户在文本中间进行编辑,则光标会在下一个onValueChanged时跳回到末尾。


44
做一件很简单的事情却要花费巨大的精力,真是太痛苦了。在我看来,将光标放在末尾应该是默认行为。至少,TextField 应该有一个可选的构造函数参数 autoFocus: AutoFocus = AutoFocus.Start,这样开发者就不必强制自己编写解决方案。 - kc_dev
@kc_dev 抱歉,如果这不是您需要的内容,但我认为我的评论可能会有用。您还可以使用TextField的textStyle构造函数参数来更改光标的位置。光标将遵循textStyle的textAlign参数。TextField(... textStyle = TextStyle(... textAlign = TextAlign.Start)) - Inliner
1
光标将跟随对齐方式的一侧,但不会移动到文本的正确位置。我更新了代码,如果您想要将光标放在已有文本选择的末尾,则必须使用选择功能。对齐只会将文本与光标对齐,而不是将光标放在文本中。 - 2jan222
那么如果我想使用其中一个不使用TextFieldValue的函数重载,怎么办?如果没有其他选项,那么其他重载就会自动变得无用。Compose已经稳定运行了近2年,这怎么能被接受呢? - Mark

0

还有一种方法可以控制光标位置,而不必使用LaunchedEffect来调用focusRequester.requestFocus()。您可以在onGloballyPositioned中调用它。如果需要,您也可以在onGloballyPositioned中显示键盘。


    val focusRequester = remember { FocusRequester() }
    val keyboard = LocalSoftwareKeyboardController.current

    val textFieldValue = TextFieldValue(text = initValue, selection = TextRange(initValue.length)) //place cursor at the end of the text
    var tfv by remember { mutableStateOf(textFieldValue) }

    TextField(
        modifier = Modifier
            .focusRequester(focusRequester)
            .onGloballyPositioned {
                focusRequester.requestFocus() // IMPORTANT
                keyboard?.show()
            },
        value = tfv,
        onValueChange = {
            tfv = it
        }
    )

0
当输入不仅是通过用户更新,而且还从组件外部更改时,这是一种在让用户玩弄光标的同时更新内容的方法:
private fun InputField(input:String) {
 
  val inputLength = input.length
  val textFieldValue = TextFieldValue(input, TextRange(inputLength))
  var textFieldValueRembered by remember { mutableStateOf(textFieldValue) }

  //a way to detect a reformation of text while letting the user control the cursor location
  if (input != textFieldValue.text)
    textFieldValueRembered = textFieldValue

  TextField(
   textFieldValue = textFieldValueRembered,
    onValueChange = {
      onValueChange(it.text)
      phoneInputField = it
    }
  )

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