I have researched this for a couple hours now, and even though there is a lot about "multiple controllers", none of the solutions worked for my problem. I don't know if I am just structuring my program wrong, so I need some input.
I got a "MainView" (App class with AppView and AppController) and a "SubView" (SubView and SubViewController). In App, I am loading the mainView as primaryStage and loading the AppController. I created a stageManager class to handle different views. My problem is: Where and how do I create/load my subViewController?
public class App extends Application {
private Stage primaryStage;
private AnchorPane rootLayout;
private AppController appController;
MainModel model = new MainModel();
StageManager stageManager;
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("App");
initRootLayout();
appController.initModel(model);
appController.setStageManager(stageManager);
}
public void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("mainView.fxml"));
rootLayout = (AnchorPane) loader.load();
appController = loader.<AppController>getController();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
stageManager = new StageManager(primaryStage, rootLayout, appController);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
public class AppController implements Initializable {
private MainModel model;
private StageManager stageManager;
@FXML
private Button btn;
@FXML
private void handleButtonAction(ActionEvent event) {
stageManager.changeScene("subView.fxml");
}
@Override
public void initialize(URL url, ResourceBundle rb) {
}
public void initModel(MainModel model) {
this.model = model;
}
public void setStageManager(StageManager stageManager){
this.stageManager = stageManager;
}
}
public class StageManager {
private Stage primaryStage;
private AnchorPane rootLayout;
private AppController appController;
public StageManager(Stage primaryStage, AnchorPane rootLayout, AppController appController){
this.primaryStage = primaryStage;
this.rootLayout = rootLayout;
this.appController = appController;
}
public void changeScene(String scene){
try {
Parent parentPane = FXMLLoader.load(getClass().getResource(scene));
primaryStage.getScene().setRoot(parentPane);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class SubController implements Initializable {
private MainModel model;
@Override
public void initialize(URL url, ResourceBundle rb) {
}
public void setModel(MainModel model){
this.model = model;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>
<AnchorPane id="AnchorPane" prefHeight="524.0" prefWidth="850.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.SubController">
<children>
<Label fx:id="lbl" layoutX="52.0" layoutY="13.0" prefHeight="38.0" prefWidth="214.0" text="just a label">
<font>
<Font name="Consolas" size="24.0" />
</font>
</Label>
</children>
</AnchorPane>
I tried the following things to load the other controller:
First I tried doing it the same way I am loading the appController (since that works), but without setting the view as the scene. So in the initRootLayout() method I simply added:
FXMLLoader subloader = new FXMLLoader();
subloader.setLocation(getClass().getResource("subView.fxml"));
subController = loader.<SubController>getController();
This didn't work. In fact something really weird happened. When I tried calling a method from subController, I got a NullPointerException. Debugging revealed that the subController is being loaded, but right after its loaded it disappears and subController Object is just "null". Why is that?
The next thing I tried after reading about this approach was to add the subController as a FXML variable. In the AppController I added @FXML private SubController subController; and in the AppController's initialize() method I tried making a call from the controller:
@Override
public void initialize(URL url, ResourceBundle rb) {
subController.setModel(model);
}
In this case, I am getting a NullPointerException, but it is actually pointing to the line where I call appController.initModel(model); in the App class. Even though this line works fine when I am not trying to use the subController.
So, I am extremely confused and I think I need a whole new structure for this. Any kinda help would be greatly appreciated! :)
EDIT:
This seems to be working: I changed the changeScenes() method to this:
public void changeScene(String scene){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(scene));
Parent root = (Parent) loader.load();
subViewController = loader.getController();
subViewController.setModel(mainModel);
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}