0

I'm using KeyboardType.Decimal for the keyboard type in the keyboardOptions entry shown below for an OutlinedTextField, but it's allowing multiple decimals in the typed number. And doing any conversions on such an input like "2.5.8".toDouble() throws a multiple points exception shown below. How can I ensure that only one decimal point is allowed and any subsequent decimal key presses don't do anything?

Composable

CustomOutlinedTextField(
    fieldModifier = Modifier
        .width(100.dp)
        .onFocusChanged {
            if (!it.isFocused) {
                if (mainViewModel.shoppingListItemState.value.quantity == "" || mainViewModel.shoppingListItemState.value.quantity == "0") {
                    mainViewModel.setStateValue(
                        ITEM_QUANTITY_STR,
                        "1"
                    )
                }
            }
        }
        .onPreviewKeyEvent {
            if (it.key == Key.Tab && it.nativeKeyEvent.action == ACTION_DOWN) {
                focusManager.moveFocus(FocusDirection.Right)
                true
            } else {
                false
            }
        },
    label = ITEM_QUANTITY_STR,
    inputVal = mainViewModel.shoppingListItemState.value.quantity,
    isSingleLine = true,
    keyboardOptions = KeyboardOptions.Default.copy(
        capitalization = KeyboardCapitalization.None,
        autoCorrect = false,
        keyboardType = KeyboardType.Decimal,
        imeAction = ImeAction.Next
    ),
    keyboardActions = KeyboardActions(
        onNext = { focusManager.moveFocus(FocusDirection.Right) }
    )
) { value ->
    mainViewModel.setStateValue(ITEM_QUANTITY_STR, value)
}

Exception

 java.lang.NumberFormatException: multiple points
Raj Narayanan
  • 2,443
  • 4
  • 24
  • 43
  • 1
    Check inside `onValueChanged` block if the text already have a decimal point or not. If already there is a period, just ignore it and submit the value to state observer. – philoopher97 Dec 15 '22 at 03:44

2 Answers2

2

You can use a regex in the onValueChange to restrict the allowed character to a decimal number.

Something like:

val pattern = remember { Regex("^\\d*\\.?\\d*\$") }

TextField(
    value = text,
    onValueChange = {
        if (it.isEmpty() || it.matches(pattern)) {
            text = it
        }
    },
    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
)

enter image description here

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
1

You can check the number of decimalSeparator in the onValueChange of your TextField and allow only one. It is advisable to get the decimalSeparator instead of comparing with . since it might change based on device Locale. Below is a sample implementation of a TextField which checks and allows only one decimalSeparator.

val decimalFormat = DecimalFormat.getInstance(Locale.getDefault()) as DecimalFormat
val decimalSeparator = decimalFormat.decimalFormatSymbols.decimalSeparator

var text by remember { mutableStateOf("123") }

TextField(
    value = text,
    keyboardOptions = KeyboardOptions.Default.copy(
        keyboardType = KeyboardType.Decimal
    ),
    onValueChange = { value ->
        val counter = value.count { it == decimalSeparator }
        if (counter <= 1) { text = value }}
)
Bullionist
  • 2,070
  • 3
  • 25
  • 42