リスナーを使用して、無効な値が動作するユーザーによって入力された場合、有効な値に戻すのアプローチがありますが、テキストフィールドのtextProperty
上の他のリスナーを持っている場合、それは問題を作成することができます。それらのリスナーは無効な値と有効な値を観察するため、無効な値を除外することを知る必要があります。
より良い方法は、TextFormatter
を使用することです。 TextFormatter
は、2つのことを行うことができます。
- は拒否、または変更できる「フィルター」を定義し、
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を参照してください。)
ObjectProperty<Double> valueProperty()
を持つフォーマッタから直接(コンバータが提供する)double値を取得して設定できます。あなたのようなことをすることができます
// 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です。 2番目のテキストフィールドはちょうどそこにあるので、フォーカスを別のコントロールに移動する効果を見ることができます(値が変更されると、値を "コミット"し、テキストフォーマッタでリスナーを呼び出します。プレス入力)。
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);
}
}
リスナーを使用しないでください。 textプロパティの他のオブザーバは、無効な入力が元に戻される前にそれを見ます。代わりに 'TextFormatter'を使用してください。 –