17
votes

I just started using JavaFX Scene Builder to build a small application.

It is made up of a controller class 'Login.java' which belongs to 'login.fxml', in which the FXML file 'registrierung.fxml' is loaded via a method called 'registrationClicked(ActionEvent event)':

public class Login {

@FXML
private void registrationClicked(ActionEvent event){
    try{
        ((Node) (event.getSource())).getScene().getWindow().hide();
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/fxml/registrierung.fxml"));
        Parent root = (Parent) loader.load();
        Stage stage = new Stage();
        Scene scene = new Scene(root);      
        stage.setTitle("Registration");
        stage.setScene(scene);
        stage.setResizable(false);
        stage.show();
    } catch(IOException e){
        e.printStackTrace();
    }
}

Now I want to get a reference to the stage of 'registrierung.fxml' in the controller class 'Registrierung.java' via the root node vboxRoot:

@FXML
private VBox vboxRoot;

Stage stage = (Stage) vboxRoot.getScene().getWindow();

However 'getScene()' always leads to a NullPointerException. The controller classes for both FXML files are adjusted in Scene Builder.

This is how I set up the rood node in 'registrierung.fxml':

<VBox fx:id="vboxRoot" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="267.0" prefWidth="355.0" stylesheets="@../css/styles.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="businesslogik.Registrierung">

What am I doing wrong?

3
Is your vboxRoot initialized? - CubeJockey
I just quoted the way I set up the root node in the fxml file and in the controller class. Please take a look. - The_Dude
Where is that code? In an event handler? - James_D
At the moment it's a field in the 'Registrierung.java' controller class. I'm new to JavaFX, but for me it seems to be in the right position. - The_Dude
Since vboxRoot is injected by the FXMLLoader, it cannot possibly be initialized until after the controller is created. Hence it is null here. Moreover, the root of the FXML is not placed in a Scene (or, consequently, a Stage) until after the FXMLLoader's load method is completed (just look at the order of your code in registrationClicked(...)). So you cannot possibly access theScene or the Stage until after the load process (including the initalize() method) is complete. Access the window only when you need it, which is likely in an event handler. - James_D

3 Answers

10
votes

you are trying to get the scene for an object that has not been initialized yet. if you were doing the same operation in

@Override 
    public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
    Stage stage = (Stage) vboxRoot.getScene().getWindow();
}

or if you have an event that triggers once you click something (which executes after the scene has loaded)

@FXML
private void action(ActionEvent event) throws IOException {
    Stage stage = (Stage) vboxRoot.getScene().getWindow();
}

This would work!

9
votes

I have run into this issue and have found by placing a call to a method like this (When the scene becomes visible and is attached to the node, this will fire):

 private void determinePrimaryStage() {
        rootPane.sceneProperty().addListener((observableScene, oldScene, newScene) -> {             
            if (oldScene == null && newScene != null) {
                // scene is set for the first time. Now its the time to listen stage changes.
                newScene.windowProperty().addListener((observableWindow, oldWindow, newWindow) -> {
                    if (oldWindow == null && newWindow != null) {
                        primaryStage = (Stage)newWindow;
                    }
                });
            }
        });
    }`

Then I can do something like this later:

if(primaryStage == null) {
    Platform.runLater(()-.{......
}else {
   //do whatever
}

Hope this helps.

2
votes

Implementing the Initializable interface did not work for me(Java 8). The method getScene() always returned null for me. So i had to do the following:

FXMLLoader loader = new FXMLLoader(getClass().getResource("MyGui.fxml"));
Parent root = (Parent)loader.load();
//do stage and scene stuff - i skip it here
MyController controller = (MyController)loader.getController();
stage.setOnShown(controller::adjustUI);

And in the controller i have:

public void adjustUI(WindowEvent event) {
    Scene scene = myComponent.getScene();
    //do stuff and do ui adjustments here
}