How to understand and use `<fx:root>` , in JavaFX?

Solution 1:

<fx:root> provides a solution to the issue of defining a reusable component with FXML.

As an example, imagine you want to define a simple custom component consisting of a TextField and Button contained in an HBox. You need this to be represented by a subclass of Node, so you can write code like

VBox vbox = new VBox();
vbox.getChildren().add(new MyComponent());

The issue is you need a Java class which is a subclass of Node, as well as the FXML. In pure Java (no FXML), you could do this with:

public class MyComponent extends HBox {
    private TextField textField ;
    private Button button ;

    public MyComponent() {
        textField = new TextField();
        button = new Button();
        this.getChildren().addAll(textField, button);
    }
}

Using FXML to define the custom component without the <fx:root> element presents a problem, because you need the FXML to be some kind of node, and then another node instance to represent the class wrapping it:

<HBox>
<TextField fx:id="textField"/>
<Button fx:id="button" />
</HBox>

and

public class MyComponent extends HBox {
    @FXML
    private TextField textField ;
    @FXML
    private Button button ;
    public MyComponent() {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("MyComponent.fxml"));
            loader.setController(this);
            HBox hbox = loader.load();
            this.getChildren().add(hbox);
        } catch (IOException exc) {
            // handle exception
        }
    }
}

This results in MyComponent consisting of an HBox wrapping an HBox wrapping the TextField and Button. The additional, redundant HBox is a result of needing one Node for the FXML root and one Node to represent the component.

<fx:root> gives a mechanism to create the Node as the component (the Java class) and then to instruct the FXML file to use that node as its root:

<fx:root type="javafx.scene.layout.HBox">
<TextField fx:id="textField" />
<Button fx:id="button" />
</fx:root>

and

public class MyComponent extends HBox {
    @FXML 
    private TextField textField ;
    @FXML
    private Button button ;
    public MyComponent() {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("MyComponent.fxml"));
            loader.setController(this);
            loader.setRoot(this);
            loader.load();
        } catch (IOException exc) {
            // handle exception
        }
    }
}

Now MyComponent has the same structure as the original all-Java version, an HBox containing a TextField and a Button. You can't do this using FXML without the <fx:root> element.