I want to add validation in javafx TextField such that user should only be able to insert integer values ([0-9] and Dot ). Also user should be able to insert either B or b(for B
You can listen to changes in the text property to check for valid inputs. Personally I prefer the user being able to input any string and not preventing any edits until the user commits the edit.
The following example is for BigInteger
(for simplicity) only and allows any number starting with non-zero and followed either only by digits or by digits that are grouped to 3 digits by seperating them with ,
. It adds the CSS class invalid
, if the input is not valid and converts it to a string containing only digits if the user presses enter:
// regex for matching the input and extracting the parts
private static final Pattern NUMBER_PATTERN = Pattern.compile("([1-9](?:\\d*|\\d{0,2}(?:\\,\\d{3})*))([tbmk]?)", Pattern.CASE_INSENSITIVE);
// map from suffix to exponent for 10
private static final Map SUFFIX_EXPONENTS;
static {
Map prefixes = new HashMap<>();
prefixes.put('k', (byte) 3);
prefixes.put('m', (byte) 6);
prefixes.put('b', (byte) 9);
prefixes.put('t', (byte) 12);
SUFFIX_EXPONENTS = Collections.unmodifiableMap(prefixes);
}
private static BigInteger convert(String s) {
if (s == null) {
return null;
}
Matcher m = NUMBER_PATTERN.matcher(s);
if (!m.matches()) {
return null;
}
String numberString = m.group(1).replace(",", "");
String suffix = m.group(2);
BigInteger factor = suffix.isEmpty() ? BigInteger.ONE : BigInteger.TEN.pow(SUFFIX_EXPONENTS.get(Character.toLowerCase(suffix.charAt(0))));
return new BigInteger(numberString).multiply(factor);
}
@Override
public void start(Stage primaryStage) throws Exception {
TextField tf = new TextField();
tf.getStyleClass().add("invalid");
// property bound to the current number in the TextField or null, if invalid
ObjectProperty numberProperty = new SimpleObjectProperty<>();
// Binding reevaluated on every change of the text property.
// A listener could be used instead to change the text to the
// previous value, if the new input is invalid.
numberProperty.bind(Bindings.createObjectBinding(() -> convert(tf.getText()), tf.textProperty()));
// change styleclass, if the string becomes (in)valid input
numberProperty.addListener((observable, oldValue, newValue) -> {
if (oldValue == null) {
tf.getStyleClass().remove("invalid");
} else if (newValue == null) {
tf.getStyleClass().add("invalid");
}
});
// handle user pressing enter
tf.setOnAction(evt -> {
BigInteger num = numberProperty.get();
tf.setText(num == null ? null : num.toString());
});
Pane root = new StackPane(tf);
Scene sc = new Scene(root, 300, 300);
sc.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(sc);
primaryStage.show();
}
In the stylesheet I set the background for invalid textfields to red-tone to give the user visual feedback:
.text-field.invalid {
-fx-background-color: #f55;
}
If you want to prevent users from inputing anything that cannot be made a valid string by apending chars, you could remove numberProperty
and everything related to it and add a listener that reverts to the old value instead:
tf.textProperty().addListener((observable, oldValue, newValue) -> {
if (isInvalid(newValue)) {
tf.setText(oldValue);
}
});