使用侦听器并在用户输入无效值时恢复为有效值的方法将起作用,但如果文本字段上有其他侦听器,则可能会产生问题textProperty
。这些侦听器将观察无效值和有效值,因此他们必须知道过滤掉任何无效值。
更好的方法是使用TextFormatter http://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TextFormatter.html. The TextFormatter
可以做两件事:
- 定义一个“过滤器”,它可以否决或修改对
TextField
's text
- 定义一个“转换器”,它定义如何在文本与任何特定类型的值之间进行转换(例如
Double
)在你的情况下。
定义适当的过滤器可能很棘手:您希望允许用户进行任何合理的编辑。这意味着用户在编辑时文本可能处于无效状态;例如您可能希望允许文本字段完全为空,即使这并不代表有效值。 (否则,例如,如果用户想要将“1”更改为“2”,这会让用户感到厌烦。)类似地,您可能希望允许“-”和“.”等内容。
这是一个例子。如果需要,过滤器应该修改传递给它的更改,并且可以返回null
完全否决变更。此示例只是检查文本是否表示有效的编辑状态,如果是,则返回未修改的更改,否则否决它。格式化程序需要处理过滤器允许的任何文本并将其转换为双精度型。这里任何不完整的东西都被表示为零。
Pattern validEditingState = Pattern.compile("-?(([1-9][0-9]*)|0)?(\\.[0-9]*)?");
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) || "-.".equals(s)) {
return 0.0 ;
} else {
return Double.valueOf(s);
}
}
@Override
public String toString(Double d) {
return d.toString();
}
};
TextFormatter<Double> textFormatter = new TextFormatter<>(converter, 0.0, filter);
TextField textField = new TextField();
textField.setTextFormatter(textFormatter);
如果需要,您可以使正则表达式更复杂,例如支持对字符进行分组("1,000.0"
)、本地化("1.003,14159"
如果这适用于语言环境),或类似科学记数法的表示("6.022E23"
等),并强制执行最小值或最大值等。您甚至可以执行诸如修改更改之类的操作,以便如果用户键入-
在文本中的任何位置,它只是翻转数字的符号。 (请参阅TextFormatter.Change文档 http://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TextFormatter.Change.html对于这种功能。)
请注意,您可以直接从格式化程序获取和设置双精度值(由转换器提供),格式化程序有一个ObjectProperty<Double> valueProperty()
。所以你可以做类似的事情
// update text field:
double value = ... ;
textFormatter.setValue(value);
// listen for changes in double value represented in text field
// Listener will be invoked when the user commits an edit:
textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
System.out.println("User entered value: "+newValue.doubleValue());
});
这是一个 SSCCE。第二个文本字段就在那里,以便您可以看到将焦点移动到不同控件的效果(如果值已更改,它会“提交”该值并调用文本格式化程序上的侦听器;如果用户按回车键)。
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.stage.Stage;
import javafx.util.StringConverter;
public class NumericTextField extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Pattern validEditingState = Pattern.compile("-?(([1-9][0-9]*)|0)?(\\.[0-9]*)?");
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) || "-.".equals(s)) {
return 0.0 ;
} else {
return Double.valueOf(s);
}
}
@Override
public String toString(Double d) {
return d.toString();
}
};
TextFormatter<Double> textFormatter = new TextFormatter<>(converter, 0.0, filter);
TextField textField = new TextField();
textField.setTextFormatter(textFormatter);
textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
System.out.println("User entered value: "+newValue.doubleValue());
});
VBox root = new VBox(5, textField, new TextField());
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 250, 250));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}