Reputation: 2128
How can I set the cursor in a random position on a TextField when it it get focus?
The equivalent of editText.setSelection(position)
with the classic android view system.
This is the code I am using to have an edit text automatically receive the focus when it is added to the screen. I would like to be able to move the cursor from the default position which is 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()
}
}
Upvotes: 16
Views: 16590
Reputation: 1924
You have to use the TextFieldValue version of TextField.
@Composable
fun TextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
/* ... */) {/* Impl */}
Code examples: EDIT: Compose Version 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() }
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()
}
}
If you have a non-empty string as an initial value you have to change the selection manually. Replace the empty TextFieldValue with: TextFieldValue(text = value, selection = TextRange(value.length, value.length))
When you want to extract the value like it is in your code. You either add the current selection as a parameter or extract it combined with the TextFieldValue. Otherwise, if the user edits in the middle of the text the cursor jumps back to the end on the next onValueChanged.
Upvotes: 19
Reputation: 6947
There is another way to control the cursor position without using LaunchedEffect
to call focusRequester.requestFocus(). You can use call it in onGloballyPositioned
. You can also show the keyboard in onGloballyPositioned
if needed.
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
}
)
Upvotes: 1
Reputation: 71
When the input is not updated just by the user and is also changed from outside of the component, this is a way to update the content while letting the user play with the cursor:
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
}
)
Upvotes: 0