I have a GUI, made using JavaFX with FXML.
This GUI has a lot of components and not all of them are needed at one moment of time.
For example, imagine a GUI that receives from its server part a list of cities. Each city is described on its own tab (and described with a lot of nodes). The set of cities contains 30 elements.
When the GUI is launched, it asks the server for a list of the cities. The server returns a random "sub-set" of cities (so, it can be Moscow + Riga + New York or St.Petersburg + Tokyo, or only Amsterdam, or all 30 cities in one set).
So. I have no need to have all 30 tabs in my node tree (I suppose they'll just "eat" memory and nothing more).
I want to manage the amount of tabs I have at each moment on my GUI.
The first simple solution I have is the following:
There are to problems I have with this solution. First, I don't know whether tabPane.getTabs().remove(index)
really removes the tab and all its content from the nodes tree. Second, all unneeded tabs will be initializated before they will be removed, so they'll use memory and resources anyway, and my GUI can be a slower than it has to be.
The second solution I have is:
But there will be way to many FXMLs, so this solution is also not useful.
The solution I dream for:
So, if someone has any ideas on this task, or knows the solution, please help my with it...
Ok, if I understood you correctly, here is my suggestion Victoria;
Suppose the main app FXML contains TabPane
somewhere in it:
// other controls
<TabPane fx:id="tabPane" id="tabPane">
<tabs>
</tabs>
</TabPane>
// other controls
In main controller:
// TabPane in fxml
@FXML
private TabPane tabPane;
// The FXMLLoader
private FXMLLoader fXMLLoader = new FXMLLoader();
// City list fetched from server
private String[] cityList = {"Moscow", "Stambul", "New York", "Bishkek"};
// OPTIONAL : Map for "city name - city fxml controller" pairs
private Map<String, Object> cityControllerMap = new HashMap<String, Object>();
// Belows are in init method
// Add only tabs dynamically but not their content
for (String city : cityList) {
tabPane.getTabs().add(new Tab(city));
}
// It is important to call it before adding ChangeListener to the tabPane to avoid NPE and
// to be able fire the manual selection event below. Otherwise the 1st tab will be selected
// with empty content.
tabPane.getSelectionModel().clearSelection();
// Add Tab ChangeListener
tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
@Override
public void changed(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue) {
System.out.println("Tab selected: " + newValue.getText());
if (newValue.getContent() == null) {
try {
// Loading content on demand
Parent root = (Parent) fXMLLoader.load(this.getClass().getResource(newValue.getText() + ".fxml").openStream());
newValue.setContent(root);
// OPTIONAL : Store the controller if needed
cityControllerMap.put(newValue.getText(), fXMLLoader.getController());
} catch (IOException ex) {
ex.printStackTrace();
}
} else {
// Content is already loaded. Update it if necessary.
Parent root = (Parent) newValue.getContent();
// Optionally get the controller from Map and manipulate the content
// via its controller.
}
}
});
// By default, select 1st tab and load its content.
tabPane.getSelectionModel().selectFirst();
If you decide to store the controllers, you may define a controller for every city fxml or define only one controller class for all of them and set it by like fXMLLoader.setController(new CommonCityController());
before loading city fxml file.
HTH.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With