I looked for a solution anywhere before asking this question, if it already exists please report it.
In practice I have this situation:
TabLayout.fxml
<JFXTabPane fx:id="tabbedPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefWidth="500.0" tabClosingPolicy="UNAVAILABLE" tabMinWidth="100.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.TAB.TabController" stylesheets="@tab.css">
<tabs>
<Tab id="anagrafeTab" closable="false" text="Anagrafe">
<content>
<fx:include fx:id="anagrafeL" source="/application/Anagrafe/AnagrafeLayout.fxml"/>
</content>
</Tab>
<Tab id="visiteTab" closable="false" text="Visite">
<content>
<fx:include fx:id="visiteL" source="/application/Visite/VisiteLayout.fxml"/>
</content></Tab>
<Tab id="latteTab" closable="false" text="Produzione Latte">
<content>
<fx:include fx:id="latteL" source="/application/Latte/LatteLayout.fxml"/>
</content>
</Tab>
<Tab id="partiTab" closable="false" text="Parti" >
<content>
<fx:include fx:id="partiL" source="/application/Parti/PartiLayout.fxml"/>
</content>
</Tab>
</tabs>
</JFXTabPane>
A layout for every "include":
AnagrafeLayout.fxml
VisiteLayout.fxml
LatteLayout.fxml
PartiLayout.fxml
Than I have a model and a controller for every Layout but an unique caller.
public class Tab {
public Tab() {
inizializza();
}
private void inizializza() {
try {
Stage stage = new Stage();
stage.setTitle("Dati");
stage.getIcons().add(new Image(getClass().getResourceAsStream("insert.png")));
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("TabLayout.fxml"));
JFXTabPane root = (JFXTabPane) loader.load();
Scene scene = new Scene(root,660,430);
stage.setScene(scene);
stage.show();
//ScenicView.show(scene);
} catch(Exception e) {
e.printStackTrace();
}
}
}
I need to pass an ObservableList<Visite> data = FXCollections.observableArrayList();
to each controller.
I do not really know how to do, and I would not use a single controller because they are quite long classes.
EDIT:
TABController.java
public class TabController implements Initializable {
@FXML
private TabPane tabbedPane;
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
assert tabbedPane != null : "fx:tabbedPane=\"in\" was not injected: check your FXML file 'TabLayout.fxml'.";
tabbedPane.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> {
if(tabbedPane.getSelectionModel().getSelectedIndex() != 0) {
tabbedPane.getScene().getWindow().setWidth(1171);
tabbedPane.getScene().getWindow().setHeight(700);
} else {
tabbedPane.getScene().getWindow().sizeToScene();
}
});
}
}
EDIT2
The 4 tabs have, as mentioned above, each his own model class. So I have 4 different lists, which are initially empty because they are filled dynamically since there is a table for each tab. The lists are created when the tabPane is called by a different internships. I need these lists because it may be the possibility of a previous load and I have to then pass this information to the controller.
In practice, this happens:
1)
Call the function insert that creates empty lists loaded from the tables in the tab. The data is then stored in a database at the close.
2)
Call the function displays that calls the function insert with a specific flag that creates lists and takes the data from the database and sends them to the tables that can then be modified.
I hope I was clear, I apologize for the ambiguity of the question precede
Now I'm solving in this way
public class TabController implements Initializable {
@FXML
private TabPane tabbedPane;
@FXML
private VisiteController visiteLController;
private ObservableList<Visite> dataVisite = FXCollections.observableArrayList();
private ObservableList<Visite> dataDelVisite = FXCollections.observableArrayList();
@FXML
private LatteController latteLController;
private ObservableList<Latte> dataLatte = FXCollections.observableArrayList();
private ObservableList<Latte> dataDelLatte = FXCollections.observableArrayList();
@FXML
private PartiController partiLController;
private ObservableList<Parti> dataParti = FXCollections.observableArrayList();
private ObservableList<Parti> dataDelParti = FXCollections.observableArrayList();
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
assert tabbedPane != null : "fx:tabbedPane=\"in\" was not injected: check your FXML file 'TabLayout.fxml'.";
tabbedPane.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> {
if(tabbedPane.getSelectionModel().getSelectedIndex() != 0) {
tabbedPane.getScene().getWindow().setWidth(1171);
tabbedPane.getScene().getWindow().setHeight(700);
} else {
tabbedPane.getScene().getWindow().sizeToScene();
}
});
visiteLController.setData(dataVisite, dataDelVisite);
latteLController.setData(dataLatte, dataDelLatte);
partiLController.setData(dataParti, dataDelParti);
}
}
and the set method obviously changed for each controller
public void setData(ObservableList<Visite> data, ObservableList<Visite> dataDel) {
this.data = data;
this.dataDel = dataDel;
liveSearh();
}
Probably the best bet here is to use a controller factory so that you can pass the list to the controllers when they are created by the FXMLLoader
.
So first, define all your controllers so they have a constructor taking the observable list:
public class TabController implements Initializable {
private final ObservableList<Visite> data ;
public TabController(ObservableList<Visite> data) {
this.data = data ;
}
// existing code...
}
and, for example,
public class AnagrafeController implements Initializable {
private final ObservableList<Visite> data ;
public AnagrafeController(ObservableList<Visite> data) {
this.data = data ;
}
// existing code...
}
Now you need a controller factory, which is essentially a function that takes a Class
and returns a controller of that class. This just needs a little reflection:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("TabLayout.fxml"));
ObservableList<Visite> data = FXCollections.observableArrayList();
loader.setControllerFactory((Class<?> controllerType) -> {
try {
Constructor<?>[] constructors = controllerType.getConstructors();
for (Constructor<?>[] constructor : constructors) {
if (constructor.getParameterCount() == 1 &&
constructor.getParameterTypes()[0] == ObservableList.class)
return constructor.newInstance(data);
}
}
// no suitable constructor found, just use default:
return controllerType.newInstance();
} catch (Exception exc) {
System.out.println("Could not create controller:");
exc.printStackTrace();
return null ;
}
});
JFXTabPane root = loader.load();
// ...
This will create an observable list (data
) and the controller factory will ensure that if the controller class has a constructor taking an observable list as a parameter, data
is passed in as that parameter.
When you use <fx:include>
the same controller factory is used to create the controllers for the included FXML files, so the same list will be passed to those controllers too, assuming you define an appropriate constructor.
You can also consider a dependency injection framework for this, which basically automates the injection of objects into your controllers. Afterburner.fx is a very nice JavaFX dependency injection framework.