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();
}
}