使用监听器,并恢复到有效的值,如果无效值是由用户输入将工作的方法,但是如果你对文本字段的textProperty
其他听众它可以创造的问题。那些听众将观察无效值和有效值,因此他们必须知道过滤掉任何无效值。
更好的方法是使用TextFormatter
。该TextFormatter
可以做两两件事:
- 定义一个“过滤器”,它可以否决或修改,到
TextField
的文本所做的任何更改
- 定义一个‘转换器’,它定义了如何转换文本与任何特定类型的值(例如
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
documentation这种功能。)
请注意,您可以直接从格式化程序获取并设置double值(由转换程序提供),该格式化程序具有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);
}
}
不要使用侦听器;文本属性的其他观察者将在恢复之前看到无效输入。改用'TextFormatter'。 –