I'm using JavaFX version 15.0.1. I want to make more complex scene via injecting several FXML files into it, like this:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane fx:controller="MainFxmlController">
<children>
<VBox>
<children>
<fx:include fx:id="topMenu" source="top_menu.fxml" />
<fx:include fx:id="navigation" source="navigation.fxml" />
<fx:include fx:id="statusBar" source="status_bar.fxml" />
</children>
</VBox>
</children>
</AnchorPane>
Here I had found that controller of included FXML is loaded automatically and injected into @FXML annotated field named <value of fx:id>Controller in the main controller (in this case MainFxmlController
).
My question is: How can I in this case use my own controller factory to instantiate the corresponding controller class? I need to give controller some dependencies in a constructor.
The same controller factory will be used for the included FXMLs as for the enclosing FXML; so your controller factory can test which controller class is passed to the callback method, create the appropriate object, and pass dependencies to it.
Something like this:
// application model class:
DataModel model = new DataModel();
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(controllerType -> {
if (controllerType == MainController.class) {
return new MainController(model);
}
if (controllerType == TopMenuController.class) {
return new TopMenuController(model);
}
if (controllerType == NavigationController.class) {
return new NavigationController(model);
}
if (controllerType == StatusBarController.class) {
return new StatusBarController(model);
}
return null ; // or throw an unchecked exception
});
Parent mainRoot = loader.load();
If you prefer (or need more generality), you can use reflection:
loader.setControllerFactory(controllerType -> {
try {
for (Constructor<?> c : controllerType.getConstructors()) {
if (c.getParameterCount() == 1
&& c.getParameterTypes()[0] == DataModel.class) {
return c.newInstance(model);
}
}
// If we got here, there's no constructor taking a model,
// so try to use the default constructor:
return controllerType.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
});