javafx-2 – JavaFX 2.2 FXML Validated TextField
I improved my previous textfield validation implementation, this time using binding for real-time validation It can be used with F XML without 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); 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,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 still a trivial point of view about the "invalid" graphic effect As you can see here:
this.invalid.addListener(new changelistener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> ov,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 try to use setstyle, but use - FX font weight: inherit; Break the code (don't why, because it should be its default) Injecting styleclass doesn't work because I can't recover it when invalid is false
Any clues? Of course, you can separate the internal listener and connect another external to other effects (f.i. displays a green tick instead of changing the textfield effect)
If you mind, you are free to use the code:)
Solution
You can always restore a style by deleting it from the style list, that is
node.getStyleClass().remove("my-style");