Reputation: 521
Given the following code:
public class Main extends Application {
private BorderPane rootLayout;
private VBox toolbarLayout;
private URL path;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
FXMLLoader loader = new FXMLLoader();
// Root View
path = getClass().getResource("mainLayout.fxml");
try {
loader.setLocation(path);
rootLayout = (BorderPane) loader.load();
} catch (IOException e){
System.out.println("Not found: " + path);
e.printStackTrace();
}
// Toolbar View
path = getClass().getResource("toolbar/toolbarView.fxml");
try {
toolbarLayout = (VBox) loader.load();
} catch (IOException e){
System.out.println("Not found: " + path);
e.printStackTrace();
}
rootLayout.getChildren().add(toolbarLayout);
Scene scene = new Scene(rootLayout);
stage.setScene(scene);
stage.show();
}
If I comment out the second fxml 'try' the rootLayout loads fine. If I comment out the borderpane and set the toolbarView as the main view it works fine too. BUT if I try to load the toolbarView into the rootLayout, the rootLayout loads fine, but the toolbarView throws an exception:
javafx.fxml.LoadException: Root value already specified.
Obviously I don't understand the fxml load process well enough, so can someone please throw some light on this? Why does it think I am trying to set the root again?
For completeness, here is the toolbarView.fxml:
<VBox fx:id="idToolbar" alignment="TOP_CENTER" maxHeight="-Infinity"
maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="400.0" prefWidth="100.0" spacing="20.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button mnemonicParsing="false" text="Button" />
<Button mnemonicParsing="false" text="Button" />
<Button mnemonicParsing="false" text="Button" />
</children>
<opaqueInsets>
<Insets />
</opaqueInsets>
<padding>
<Insets top="20.0" />
</padding>
</VBox>
Upvotes: 3
Views: 6321
Reputation: 209225
The root
property contains a reference to the structure specified by the FXML file; i.e. to the object created by the root element of the FXML file. Assuming you are not using the "dynamic root" (<fx:root>
) pattern, the root will be set as part of the load
process to the object corresponding to the root element of the FXML. If it is not null
at this stage (i.e. if it has already been set), then you will get an exception. A similar thing is true for the controller
property: if the FXML file specifies an fx:controller
attribute, the controller will be set as part of the load()
process; if it is not null
, an exception is thrown.
The FXMLLoader
is really only designed to be used once, as you have many interdependent properties which are typically set as part of the load process: root
, location
, controller
, resources
, and elements of the namespace
. So you should really create a new FXMLLoader
for each FXML file you want to load:
FXMLLoader loader = new FXMLLoader();
// Root View
path = getClass().getResource("mainLayout.fxml");
try {
loader.setLocation(path);
rootLayout = (BorderPane) loader.load();
} catch (IOException e){
System.out.println("Not found: " + path);
e.printStackTrace();
}
// Toolbar View
loader = new FXMLLoader();
path = getClass().getResource("toolbar/toolbarView.fxml");
try {
// note you omitted this line:
loader.setLocation(path);
toolbarLayout = (VBox) loader.load();
} catch (IOException e){
System.out.println("Not found: " + path);
e.printStackTrace();
}
rootLayout.getChildren().add(toolbarLayout);
It may be possible to reuse an FXMLLoader
by carefully unsetting anything that has been set as part of the previous load process:
FXMLLoader loader = new FXMLLoader();
// Root View
path = getClass().getResource("mainLayout.fxml");
try {
loader.setLocation(path);
rootLayout = (BorderPane) loader.load();
} catch (IOException e){
System.out.println("Not found: " + path);
e.printStackTrace();
}
loader.setRoot(null);
loader.setController(null);
loader.setResources(null);
loader.getNamespace().clear();
// Toolbar View
path = getClass().getResource("toolbar/toolbarView.fxml");
try {
// note you omitted this line:
loader.setLocation(path);
toolbarLayout = (VBox) loader.load();
} catch (IOException e){
System.out.println("Not found: " + path);
e.printStackTrace();
}
rootLayout.getChildren().add(toolbarLayout);
but this is really not the intended usage, and may not be robust to future changes to the FXMLLoader
implementation.
Upvotes: 4