javauser-interfacejavafxmaterial-uidesktop-application

JavaFX does not displays tooltip message


When trying to use Tooltip instead of Label in JavaFX, the validation messages are not displayed. I also tried to use some alternatives for displaying messages for TextBox controls, but not worked properly. Here is an example implementation:

@FXML
private Tooltip nameTooltip;


private boolean validateNameFields(List<String> filterParameters) {

    nameTooltip = new Tooltip();

    if (!!areAllNamesAreValid(filterParameters)) {
        nameTooltip.setText("Invalid name...");
        nameTextField.setStyle("-fx-border-color: red;");
        return false;
    } else {
        nameTooltip.setText("");
        nameTextField.setStyle("");
        return true;
    }
}

On the other hand, I would also consider to use some libraries e.g. Material to make such implementation. Is there any suitable alternatives for this purpose used in JavaFX?


Solution

  • Never set an @FXML annotated field to another value.

    Ensure the tooltip is set on your target field either in FXML or Java.

    Here is an example of how to set a Tooltip in Java based on the code in your question.

    Delete any mention of nameTooltip in your FXML, as well as the @FXML annotation on nameTooltip in your Java code.

    Modify your provided Java code to:

    // other code where you properly initialize nameTextField.
    // . . .
    // validation logic.
    
    private Tooltip nameTooltip = new Tooltip("Invalid name...");
    
    private boolean validateNameFields(List<String> filterParameters) {
        if (!areAllNamesAreValid(filterParameters)) {
            nameTextField.setTooltip(nameTooltip);
            nameTextField.setStyle("-fx-border-color: red;");
            return false;
        } else {
            nameTextField.setTooltip(null);
            nameTextField.setStyle("");
            return true;
        }
    }
    

    When we need to add some tooltips from Scene Builder and set its properties, then we need to create a variable on the JavaFX controller in order to set these tooltip for a control.

    You only need to provide a reference in Java, so that the FXML loader can inject the value of the Tooltip that it creates from the FXML file definition. You don't (and should not) also create another value in Java, reassigning the reference value which was injected by the loader.

    Here is an alternate implementation which defines a tooltip in FXML and references the tooltip in Java, rather than creating the tooltip in Java. To use the app, run it and hover over the label, then click the button and hover over the label again, you will see that every time you click the button, the tooltip changes.

    TipApp.java

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    public class TipApp extends Application {
        @Override
        public void start(Stage stage) throws IOException {
            FXMLLoader fxmlLoader = new FXMLLoader(TipApp.class.getResource("tip-view.fxml"));
            Scene scene = new Scene(fxmlLoader.load(), 320, 240);
            stage.setTitle("Hello!");
            stage.setScene(scene);
            stage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    }
    

    TipController.java

    import javafx.fxml.FXML;
    import javafx.scene.control.Tooltip;
    
    public class TipController {
        @FXML
        private Tooltip tooltip;
    
        private int numClicks = 0;
    
        @FXML
        protected void onButtonClick() {
            ++numClicks;
            tooltip.setText("Button clicked " + numClicks + " times.");
        }
    }
    

    TipView.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.geometry.Insets?>
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.control.Tooltip?>
    <?import javafx.scene.layout.VBox?>
    
    <VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.tipapp.TipController">
        <padding>
            <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
        </padding>
    
        <Label text="Hover for tooltip">
          <tooltip>
             <Tooltip fx:id="tooltip" text="Welcome to the tip application." />
          </tooltip></Label>
        <Button onAction="#onButtonClick" text="Update tooltip text" />
    </VBox>
    

    In terms of what is a "better way" (defining tooltips in Java code like the first example in this answer or in FXML like the second example), I think that depends on preference, either can work.


    Separate question on validation libraries

    Is there any suitable alternatives for this purpose used in JavaFX?

    Library recommendations are out of scope, but to give you an indication, you could investigate MaterialFX or ControlsFX.

    MaterialFX includes a validation interface. I have never used it.

    ControlsFX supports validation. ControlsFX graphic validations may not interact well with tooltip validation if you also use a tooltip for its intended purpose of providing info on the field. But usually you wouldn't have such a strange setup and a StyleClassDecoration will work in any case.

    For some of the core validation work (not the UI related stuff), you could consider something like Bean Validation via the Hibernate Validator reference implementation, depending on the extent of your validation requirements. It integrates with JavaFX properties through value extractors.

    Spring validation will use hibernate validation if it is on the class path, and hibernate validation will enable JavaFX value extractors for JavaFX properties in Java beans if JavaFX is on the classpath.

    Even though the reference implementation for bean validation has hibernate in the name, it has no requirement on the hibernate ORM technology. It may be too complicated and overkill for what you need and you may find it difficult to integrate with your UI. But if you have large and complex valuation needs, it might provide a better framework for implementing some of those validation features than you could implement yourself.