How to switch the root from controllers without re

2019-08-30 05:36发布

问题:

(James_D has showed me the solution. I'll just elaborate on how it helped me and what I did at the end of this question. Hopefully others find it useful.)

My wrong initial approach:

There are several FXML files and their controllers in my program. I loaded the FXML files and changed the scenes. It caused the window size to change when it was maximized.

Then this answer showed that I should change the root instead.

Current approach and question:

I tried the code given in the answer there and it works flawlessly. First I set a root in the start method and switched to anther root after a time delay. It's just to check. It looks like this:

package com;

import java.io.IOException;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Launcher extends Application {

public static void main(String[] args) {
    launch(args);

}

@Override
public void start(Stage stage) throws Exception {

    StackPane root = FXMLLoader.load(getClass().getResource("/com/Scene2.fxml"));
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.setMaximized(true);
    stage.show();

    Timeline timeline2 = new Timeline(new KeyFrame(Duration.millis(1000), e2 -> {

        try {
            StackPane root2 = FXMLLoader.load(getClass().getResource("/com/Scene1.fxml"));
            scene.setRoot(root2);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }));
    timeline2.play();

}

}

In my actual program the start method loads an FXML file. Later I click buttons or something on the screen which should change the root. So I'll have to use a similar code in controllers. But to set the root, I need to get the scene. In the start method the scene is already created and available. I can't figure out how to get that in a controller. So I tried creating a new scene in the controller. But it's not working. It doesn't throw exceptions or anything. The program simply doesn't do anything when I press the button. This is the relevent code in the controller.

public void changeRoot(ActionEvent event) throws IOException {
        try {               
            Parent root2 = FXMLLoader.load(getClass().getResource("/com/Scene2.fxml"));

            // I added the line below.
            Scene scene = new Scene(root2);   

            scene.setRoot(root2);

        } catch (IOException e1) {
            e1.printStackTrace();
        }

    }

The solution:

Right then. As you can see from the comment below, I can get the scene by calling getScene() on any node in the current scene. In my example code I have a button on the current screen. A button is a node. So I use it to get the scene. I'll just share the code of the controller class so you know what I mean.

package com;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Scene1Controller implements Initializable {

@FXML
private Button button;

@Override
public void initialize(URL location, ResourceBundle resources) {


}

    public void changeRoot(ActionEvent event) throws IOException {
        try {               
            Parent root2 = FXMLLoader.load(getClass().getResource("/com/Scene2.fxml"));

            button.getScene().setRoot(root2);     // Here I get the scene using the button and set the root.

        } catch (IOException e1) {
            e1.printStackTrace();
        }

    }

}

Now there's something you should keep in mind. You have to make sure you give an fx:id for the node in FXML. It should match the given variable name of the node in the controller. For example, in my controller I have a Button. I've named it 'button'. I've also made sure the id for the Button in the FXML file is 'button'. It's very easy to set the fx:id. I used the Gluon Scene Builder. (Sometimes the changes I make there doesn't get updated in Eclipse right away. Refreshing the project makes sure the changes are updated in Eclipse too.)