Search code examples
androidkotlinandroid-jetpack-composeandroid-softkeyboardandroid-compose-textfield

java.lang.NumberFormatException: multiple points with KeyboardType.Decimal


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

Solution

  • 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 }}
    )