A FXMLLoader that is useful and optimized for one-time load of a fxml-file is not useful for many usecases in which you want to produce an arbitrary number of instances of the scenegraph defined in a fxml-file.
1)
FXMLLoader loader = new FXMLLoader();
loader.setLocation(someFXMLFile);
loader.load();
-> makes the loader instance *bound* to the instance of the loaded fxml. It can be reset like this:
loader.setController(null);
loader.setRoot(null);
but this only works when there are no bindings in the fxml file. When there are bindings, all subsequent calls to load() are binding their properties to the latest controller.
2) Performance issues. Between two calls to load() no caching is done and the performance is low. See RT-23413
3) Inconvenience. The menchanism takes a lot of tweaks.
Because of this, implement a builder-like FXMLLoader (i just call it "FXMLBuilder" for now)
the usage would be like this:
FXMLBuilder builder = new FXMLBuilder("example.fxml"); // initiates the builder with the given fxml-file. The file is parsed only *once*. Caching is done effectively.
FXMLWrapper wrapper = builder.build(); // a wrapper is needed because you have two return values: the root of the graph and the controller that is associated with it
Controller controller = wrapper.getController(); // retrieve the controller
Node root = wrapper.getRoot(); // retrieve the root
// do some stuff with them, save them, whatever
...
// later:
wrapper = builder.build(); // new call to build, this doesn't invoke reflection a second time thus making the performance fast.
controller = wrapper.getController(); // retrieve another controller that has nothing to do with the one above
root = wrapper.getRoot(); // retrieve another root that has nothing to do with the one above
Conclusion:
The FXMLLoader's principle is to be bound to the *instance* of the loaded fxml-file, thus making the reuse of it unpracticable.
The FXMLBuilder's princriple is to be bound to the *fxml-file*, thus making the reuse of it easy.
1)
FXMLLoader loader = new FXMLLoader();
loader.setLocation(someFXMLFile);
loader.load();
-> makes the loader instance *bound* to the instance of the loaded fxml. It can be reset like this:
loader.setController(null);
loader.setRoot(null);
but this only works when there are no bindings in the fxml file. When there are bindings, all subsequent calls to load() are binding their properties to the latest controller.
2) Performance issues. Between two calls to load() no caching is done and the performance is low. See RT-23413
3) Inconvenience. The menchanism takes a lot of tweaks.
Because of this, implement a builder-like FXMLLoader (i just call it "FXMLBuilder" for now)
the usage would be like this:
FXMLBuilder builder = new FXMLBuilder("example.fxml"); // initiates the builder with the given fxml-file. The file is parsed only *once*. Caching is done effectively.
FXMLWrapper wrapper = builder.build(); // a wrapper is needed because you have two return values: the root of the graph and the controller that is associated with it
Controller controller = wrapper.getController(); // retrieve the controller
Node root = wrapper.getRoot(); // retrieve the root
// do some stuff with them, save them, whatever
...
// later:
wrapper = builder.build(); // new call to build, this doesn't invoke reflection a second time thus making the performance fast.
controller = wrapper.getController(); // retrieve another controller that has nothing to do with the one above
root = wrapper.getRoot(); // retrieve another root that has nothing to do with the one above
Conclusion:
The FXMLLoader's principle is to be bound to the *instance* of the loaded fxml-file, thus making the reuse of it unpracticable.
The FXMLBuilder's princriple is to be bound to the *fxml-file*, thus making the reuse of it easy.