You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用KeyboardType.Decimal时多小数点输入报错,如何限制仅输入一个小数点?

解决Compose Decimal键盘允许输入多个小数点的问题

问题描述

我在Compose的CustomOutlinedTextField中设置了KeyboardType.Decimal作为键盘类型,但该配置允许输入多个小数点。当对诸如"2.5.8"这类输入执行toDouble()转换时,会抛出java.lang.NumberFormatException: multiple points异常。需要确保仅允许输入一个小数点,后续按下小数点按键时不产生任何响应。

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

抛出异常

java.lang.NumberFormatException: multiple points

解决方案

方法一:在值变更回调中过滤输入(推荐)

直接在onValueChange回调里处理输入内容,从根源确保最多只有一个小数点,避免后续转换异常:

CustomOutlinedTextField(
    // 其他参数保持不变
) { newValue ->
    val filteredValue = when {
        // 处理开头是小数点的情况,自动补0
        newValue.startsWith(".") -> "0$newValue"
        // 如果已经有小数点,只保留第一个小数点及之前的内容,后续小数点直接过滤
        newValue.count { it == '.' } > 1 -> newValue.replaceAfterFirst(".", "")
        else -> newValue
    }
    mainViewModel.setStateValue(ITEM_QUANTITY_STR, filteredValue)
}

方法二:使用VisualTransformation做视觉过滤

这种方法仅修改显示内容,不会改变实际存储的输入值,适合需要保留原始输入但显示正确格式的场景:

// 自定义VisualTransformation实现
class SingleDecimalVisualTransformation : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        val filteredContent = if (text.text.contains(".")) {
            // 保留第一个小数点,过滤后续所有小数点
            val firstDotIndex = text.text.indexOf('.')
            text.text.take(firstDotIndex + 1) + text.text.substring(firstDotIndex + 1).filter { it != '.' }
        } else {
            text.text
        }
        return TransformedText(AnnotatedString(filteredContent), OffsetMapping.Identity)
    }
}

// 在CustomOutlinedTextField中应用
CustomOutlinedTextField(
    // 其他参数不变
    visualTransformation = SingleDecimalVisualTransformation(),
) { value ->
    mainViewModel.setStateValue(ITEM_QUANTITY_STR, value)
}

注意:方法二只是视觉上隐藏多余的小数点,实际存储的quantity值仍可能包含多个小数点,因此如果需要后续执行toDouble()转换,更推荐使用方法一。


内容的提问来源于stack exchange,提问作者Raj Narayanan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:45:32