Also, an animation is applied to smooth the display of the visual cue.
The prior posts can be found here
- Disabling a Save Button with JavaFX Confirmation Binding Controls
- Applying Email Validation to a JavaFX TextField Using Binding
This video demonstrates the latest iteration.
In order for the input to be accept, the TextFields must match and contain a valid email address. Initially, the controls are empty and no visual cue is provided. When the user starts typing in Email or Confirm Email, a red rectangle with an X is displayed. Once the user finishes typing a valid email in both the Email and Confirm Email TextFields, a green rectangle with an = is displayed. At this point, the Save Button is also enabled.
Both the display of the red rectangle and the brief display of the green rectangle are animated, since it looked far too jarring to have them appear suddenly
The source for the project can be found in a zip file here.
Scene Builder
The column holding the rectangles has been added to the GridPane as a VBox / Label pair that spans both rows. This screenshot of Scene Builder shows the column. Before leaving Scene Builder, I set the Opacity of the VBox to 0 which hides it.Scene Builder with Visual Cue Column Showing |
CSS
I'm using CSS because I plan on keeping the same VBox and Label in place for both the red X and the green equals. I do this mainly to prevent any type of glitchy resizing that would occur if I attempted to add or remove controls from the container. So, I toggle between two styles for each of the two controls.Notice the -fx-border-radius and -fx-background-radius settings that give the VBoxes the same look-and-feel as the standard TextFields.
In Scene Builder, I also put a small top and bottom margin of 2 on the VBox to make it match the TextFields.
.vbox-valid-error { -fx-background-color: #ff9999; -fx-border-color: #ff3300; -fx-background-radius: 2; -fx-border-radius: 2; } .label-valid-error { -fx-text-fill: #ff0000; } .vbox-valid-ok { -fx-background-color: #99ff99; -fx-border-color: #33ff00; -fx-background-radius: 2; -fx-border-radius: 2; } .label-valid-ok { -fx-text-fill: #00ff00; }
Code
This is the @FXML initialize() method of the JavaFX Controller class.@FXML public void initialize() { EmailValidator emailValidator = EmailValidator.getInstance(); StringBinding validEmailExpr = Bindings.createStringBinding( () -> emailValidator.isValid(txtEmail.getText())?txtEmail.getText():"", txtEmail.textProperty() ); btnSave.disableProperty().bind( txtEmail.textProperty() .isEqualTo(txtConfirmEmail.textProperty()).not() .or( txtEmail.textProperty().isEqualTo( validEmailExpr ).not() ) .or( txtEmail.textProperty().isEmpty() ) ); BooleanBinding okToAnimate = txtEmail.textProperty() .isEqualTo( validEmailExpr ).not() .or( txtEmail.textProperty().isEqualTo(txtConfirmEmail.textProperty()).not() ); okToAnimate.addListener( (obs, ov, nv) -> { logger.info("txtEmail changed from " + ov + " to " + nv); FadeTransition ft = null; if( nv ) { // ok to show -> fade in ft = new FadeTransition(Duration.millis(500), vboxValid); ft.setFromValue(0.0d); ft.setToValue(1.0d); } else { if( !txtEmail.getText().isEmpty() ) { vboxValid.getStyleClass().clear(); vboxValid.getStyleClass().add("vbox-valid-ok"); lblValid.getStyleClass().clear(); lblValid.getStyleClass().add("label-valid-ok"); lblValid.setText("="); } ft = new FadeTransition(Duration.millis(1000), vboxValid); ft.setFromValue(1.0d); ft.setToValue(0.0d); ft.setOnFinished( (evt) -> { if( !txtEmail.getText().isEmpty() ) { vboxValid.getStyleClass().clear(); vboxValid.getStyleClass().add("vbox-valid-error"); lblValid.getStyleClass().clear(); lblValid.getStyleClass().add("label-valid-error"); lblValid.setText("X"); } }); } ft.play(); } ); }
okToAnimate is a custom binding on two conditions. Order is important when using the or() method.
- The Email TextField does not contain a valid email.
- The Email TextField and the Confirm Email TextField don't match.
The Binding to the textProperty() updates the okToAnimate value when the TextField conditions change. A ChangeListener is attached to okToAnimate.
The ChangeListener fades the red X when the user starts to enter the input. That's because even with a quick two paste operations (CtrlV/CtrlV), the interaction begins with invalid data. Once the user corrects the data, the red X changes over to a green = via a style class and a new Label. Recall that this ChangeListener is only invoked when the condition changes rather than the data, so the ChangeListener is not called with each key press.
The Opacity of the red X / green = is animated to give a smoother transition. FadeTransition is a convenience for this specific case.
In the old days, you might get a popup after-the-fact when submitting data. These days, validations give instantaneous feedback to the user, with the best validations guiding the user to the correct input. In this example, it's easy to see if the contents match, but this post could easily be extended to handle PasswordFields where matching contents couldn't be determined just by looking.
No comments:
Post a Comment