When I did data validation to limit the input data, I inherited TextField and in it overridden the replaceText () method. This allowed not only to restrict the data after entering, but also to restrict the entered characters - with the correct regexp it will be impossible to enter letters where only numbers are needed. A class was also written to control validation, the object of which was specified as property. At the output, the validator gave BooleanProperty, which allowed you to directly block interface objects. Total: two classes and some witchcraft with property. It seems to me the best option.
The code of the modified TextField looks like this:
private final ObjectProperty<ValidationChecker> onValidate = new SimpleObjectProperty<>(this, "onValidate", null); private final ObjectProperty<Predicate<String>> symbolsChecker = new SimpleObjectProperty<>(this, "symbolsChecker", null); private final ObjectProperty<Predicate<String>> textChecker = new SimpleObjectProperty<>(this, "textChecker", null); @Override public void replaceText(int start, int end, String text){ if(this.getSymbolsChecker() == null || this.getTextChecker() == null){ //Если не определеные функции проверки символов или текста //вести себя как обычное текстовое поле super.replaceText(start, end, text); return; } if(this.getSymbolsChecker().test(text)) { //Если введённый текст/символы проходят проверку //Добавляем его к уже имеющемуся тексту //symbolstChecker ограничивает вводимые символы super.replaceText(start, end, text); } if(this.getTextChecker().test(this.getText())) { //Если полный текст поля проходит проверку //Уведомляем валидатор, что всё хорошо Platform.runLater(() -> this.getOnValidate().handle(new ValidationUpdateEvent(this.getId(), true))); } else { //Иначе, уведомляем о том, что всё плохо и //какой-то контрол стоит заблокировать Platform.runLater(() -> this.getOnValidate().handle(new ValidationUpdateEvent(this.getId(), false))); } }
A little bit of explanation to the code above. symbolsChecker and textChecker are objects that implement the Predicate <String> interface, which decide whether the entered text will be added to the text field (symbolsChecker) and whether all text in the text field (textChecker) will be validated. Two checks were used because in my task it was necessary to limit the input only to numbers and plus limit the range of numbers entered. They are made through property so that you can specify them not in the code, but through the FXML file.
The validator code is extremely simple:
public class ValidationChecker implements EventHandler<ValidationUpdateEvent> private HashMap<String, Boolean> validationInfo = new HashMap<>(); private BooleanProperty isValid = new SimpleBooleanProperty(this, "isValid", true); @Override public void handle(ValidationUpdateEvent event) { this.validationInfo.put(event.getID(), event.isValid()); if(this.validationInfo.containsValue(false)){ this.isValid.set(false); } else{ this.isValid.set(true); } }
Again a bit of explanation. Each TextField has a text property ID. When attempting to enter data in a text field, a validator will be notified, which will register in HashMap the current value of the validity of the field with the specified ID. In this validator, a logical AND is implemented: if at least in one text field associated with the validator the data is not valid, then the isValid property will be false, and the control that has the disable property on the isValid validator will be blocked.