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

JavaFX数值TextField处理科学计数法输入导致的Infinity问题

问题根源

你遇到的无法编辑问题本质是两个逻辑冲突导致的:

  • 当输入值超出double范围时,Double.valueOf()会返回Infinity/-Infinity,转换器的toString()方法直接输出"Infinity"字符串
  • 该字符串不匹配你定义的数值正则规则,后续所有编辑操作的新文本都会被filter拦截,导致文本框锁死
修复方案

我们从「源头拦截超范围输入」和「异常值容错」两个维度修改代码,修改点如下:

1. 优化转换器逻辑,拦截异常值

修改StringConverter的两个方法:

  • fromString解析后校验值合法性,遇到无穷大/NaN直接返回上一次合法值
  • toString只输出符合正则规则的数值字符串,避免出现不合法的"Infinity"串

2. 监听值变化,异常值自动回滚

在TextFormatter的值监听器中判断新值合法性,一旦出现异常直接回滚到旧值

3. 优化正则规则,限制指数输入范围

double最大有效指数为308,直接限制科学计数法的指数最多3位,从输入层面避免超范围输入

修改后完整代码
import java.text.DecimalFormatSymbols;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class NumericTextFieldDemo extends Application {

    char sep = new DecimalFormatSymbols().getDecimalSeparator();
    String negStarter = new StringBuilder("-").append(sep).toString();
    String posStarter = new StringBuilder("+").append(sep).toString();
    // 优化正则:限制指数最多3位,从输入层面减少超范围可能,同时修正原代码正则语法错误
    String patternStr = new StringBuilder()
            .append("[+|-]?(([1-9][0-9]*)|0)?(")
            .append(sep)
            .append("[0-9]*)?(([e|E][+|-]?[0-9]{0,3})?)")
            .toString();
    Pattern validEditingState = Pattern.compile(patternStr);

    class NumericTextField extends TextField {
        // 存储上一次合法值,用于异常回滚
        private Double lastValidValue = 0.0;

        UnaryOperator<TextFormatter.Change> filter = c -> {
            String text = c.getControlNewText();
            if (validEditingState.matcher(text).matches()) {
                return c;
            } else {
                return null;
            }
        };

        StringConverter<Double> converter = new StringConverter<Double>() {

            @Override
            public Double fromString(String s) {
                if (s.isEmpty() || "-".equals(s) || "+".equals(s)
                        || negStarter.equals(s) || posStarter.equals(s)) {
                    return lastValidValue;
                }
                try {
                    Double value = Double.valueOf(s);
                    // 校验值合法性,无穷大/NaN直接返回上一次合法值
                    if (value.isInfinite() || value.isNaN()) {
                        return lastValidValue;
                    }
                    lastValidValue = value;
                    return value;
                } catch (NumberFormatException e) {
                    return lastValidValue;
                }
            }

            @Override
            public String toString(Double d) {
                // 避免输出Infinity等不合法字符串
                if (d == null || d.isInfinite() || d.isNaN()) {
                    return lastValidValue.toString();
                }
                lastValidValue = d;
                return d.toString();
            }
        };

        NumericTextField(double initValue) {
            lastValidValue = initValue;
            TextFormatter<Double> textFormatter = new TextFormatter<>(converter, initValue, filter);
            // 新增异常值回滚逻辑
            textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
                if (newValue == null || newValue.isInfinite() || newValue.isNaN()) {
                    textFormatter.setValue(oldValue);
                    return;
                }
                lastValidValue = newValue;
                System.out.println("User entered value: " + newValue);
            });
            setTextFormatter(textFormatter);
        }

        NumericTextField() {
            this(0.0);
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        NumericTextField ntf = new NumericTextField();
        ntf.setFont(new Font("Arial", 14));

        VBox root = new VBox(5, ntf);
        root.setAlignment(Pos.CENTER);
        primaryStage.setScene(new Scene(root, 250, 150));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
验证效果

修改后即使用户输入1e400这类超范围数值,文本框也会自动回滚到上一次合法值,不会出现锁死的情况,所有编辑、删除、Esc回退功能都可以正常使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 18:36:02