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
相关产品推荐
相关产品推荐

