JavaFX 2.2 FXML Validated TextField

I improved my previous implementation of TextField validation, this time creating a real Custom Control with real-time validation using bindings. It can be used with FXML without the need for more Java code.

import javafx.beans.binding.BooleanBinding; import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.control.TextField; import javafx.scene.effect.BlurType; import javafx.scene.effect.DropShadow; import javafx.scene.effect.Effect; import javafx.scene.paint.Color; /** * <p> * TextField with regex-based real-time input validation. * JavaFX 2 and FXML compatible. </p> * <p> * FXML code example:<div> * {@code <ValidatedTextField fx:id="validatedTextField" minLength="1" maxLength="1" mask="^[0-9]*$" />} * </div> * </p> * * @author 82300009 */ public final class ValidatedTextField extends TextField { private final BooleanProperty invalid = new SimpleBooleanProperty(false); private final StringProperty mask; private final IntegerProperty minLength; private final IntegerProperty maxLength; private Effect invalidEffect = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0); public ValidatedTextField() { super(); this.mask = new SimpleStringProperty("."); this.minLength = new SimpleIntegerProperty(-1); this.maxLength = new SimpleIntegerProperty(-1); bind(); } public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable) { this(mask, minLength, maxLength, nullable, null); } public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable, String string) { super(string); this.mask = new SimpleStringProperty(mask); this.minLength = new SimpleIntegerProperty(minLength); this.maxLength = new SimpleIntegerProperty(maxLength); bind(); } public ReadOnlyBooleanProperty invalidProperty() { return invalid; } public ReadOnlyStringProperty maskProperty() { return mask; } public ReadOnlyIntegerProperty minLengthProperty() { return minLength; } public ReadOnlyIntegerProperty maxLengthProperty() { return maxLength; } public boolean getInvalid() { return invalid.get(); } public String getMask() { return mask.get(); } public void setMask(String mask) { this.mask.set(mask); } public int getMinLength() { return minLength.get(); } public void setMinLength(int minLength) { this.minLength.set(minLength); } public int getMaxLength() { return maxLength.get(); } public void setMaxLength(int maxLength) { this.maxLength.set(maxLength); } public Effect getInvalidEffect() { return this.invalidEffect; } public void setInvalidEffect(Effect effect) { this.invalidEffect = effect; } private void bind() { this.invalid.bind(maskCheck().or(minLengthCheck())); this.textProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> ov, String t, String t1) { if (textProperty().get().length() > maxLength.get()) { setText(t); } } }); this.invalid.addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) { if (t ^ t1) { if (t1) { // setStyle("-fx-font-weight: bold; -fx-text-fill: red;"); setEffect(invalidEffect); } else { // setStyle("-fx-font-weight: normal; -fx-text-fill: inherit;"); setEffect(null); } } } }); } private BooleanBinding maskCheck() { return new BooleanBinding() { { super.bind(textProperty(), mask); } @Override protected boolean computeValue() { return !textProperty().get().matches(mask.get()); } }; } private BooleanBinding minLengthCheck() { return new BooleanBinding() { { super.bind(textProperty(), minLength); } @Override protected boolean computeValue() { return textProperty().get().length() < minLength.get(); } }; } private BooleanBinding maxLengthCheck() { return new BooleanBinding() { { super.bind(textProperty(), maxLength); } @Override protected boolean computeValue() { return textProperty().get().length() > maxLength.get(); } }; } } 

However, there is a trivial point regarding the β€œinvalid” graphic effect. As you can see here:

 this.invalid.addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) { if (t ^ t1) { if (t1) { // setStyle("-fx-font-weight: bold; -fx-text-fill: red;"); setEffect(invalidEffect); } else { // setStyle("-fx-font-weight: normal; -fx-text-fill: inherit;"); setEffect(null); } } } }); 

I tried using setStyle, but using -fx-font-weight: inherit; breaks the code (not because it should be the default). The implementation of StyleClass does not work, since I cannot return it if false is false.

Any clue? Of course, you can turn off the internal listener and attach another one outside with other effects (fi, showing a green check mark instead of changing the TextField effect).

You can use the code if you don't mind :)

+5
source share
1 answer

You can always return a style by removing it from the list of ie style classes

 node.getStyleClass().remove("my-style"); 
+3
source

Source: https://habr.com/ru/post/953905/


All Articles