Search code examples
javafxinstancestage

JavaFX - Unique scene per stage


I have a class that extends Application and calls the primary stage. This primary stage has a Next Button, that will call another stage (options stage). The options stage has a Previous Button. I'd like to get the instance of the primary stage, in the state it was before the user clicked Next Button, for example: a textfield with input data or combobox with selected item. How can I do that?

Main class:

public class MainClass extends Application {

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

        final FXMLLoader loader = new FXMLLoader(getClass().getResource("interfaceOne.fxml"));
        final Parent root = (Parent)loader.load();
        final MyController controller = loader.<MyController>getController();

        primaryStage.initStyle(StageStyle.TRANSPARENT);
        primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png")));

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        Platform.setImplicitExit(false);

        controller.setStage(primaryStage);
        primaryStage.show();        
    }

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

}

MyController:

public class MyController{

    // Some declarations ...

    Stage stage = null;

    public void setStage(Stage stage) {
       this.stage = stage;
    }

    // Next button's action
    @FXML
    public void handleNextAction(ActionEvent event) {

        try {  

            Parent root = FXMLLoader.load(getClass().getResource("optionInterface.fxml"));

            stage.initStyle(StageStyle.TRANSPARENT);
            stage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png")));
            stage.setScene(new Scene(root));
            stage.show();

            // Hide the current screen
            ((Node)(event.getSource())).getScene().getWindow().hide();

        } catch (Exception exc) {
            System.out.println("Error: " + exc.getMessage());
        }
    }
}

Options Controller:

public class OptionsController implements Initializable {

    public void handlePreviousAction(ActionEvent event) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("interfaceOne.fxml"));;
            MyController controller = MyController.getInstance();

            stage.initStyle(StageStyle.TRANSPARENT);
            stage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png")));
            stage.setScene(new Scene(root));

            controller.setStage(stage);
            controller.isLocationLoaded(false);
            stage.show();

            // Hide the current screen
            ((Node)(event.getSource())).getScene().getWindow().hide();

        } catch (IOException exc) {
            System.out.println("Error: " + exc.getMessage());

        }
    }
}

Solution

  • Recommended Approach

    Don't use multiple stages for this, instead use a single stage and multiple scenes or layered Panes.

    Sample References

    1. Angela Caicedo's sophisticated Scene switching tutorial.
    2. A wizard style configuration.

    Background

    Read over a discussion of the theater metaphor behind JavaFX to help understand the difference between a Stage and a Scene and why you want to probably be changing scenes in and out of your application rather than stages.

    Simple Sample

    I created a simple sample based upon your application description which just switches back and forth between a main scene and an options scene. As you switch back and forth between the scenes, you can see that the scene state is preserved for both the main scene and the options scene.

    For the sample, there is just a single stage reference, which is passed to the application in it's start method and the stage reference is saved in the application. The application creates a scene for the main screen and another for the options screen, saving both scene references switches the currently displayed scene back and forth between these references as required using stage.setScene.

    The demo is deliberately simple to make it easy to understand and does not persist any of the data used or make use of a MVC style architecture or FXML as might be done in a more realistic demo.

    reservation options

    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.event.*;
    import javafx.scene.*;
    import javafx.scene.control.*;
    import javafx.scene.layout.*;
    import javafx.stage.Stage;
    
    public class RoomReservationNavigator extends Application {
      public static void main(String[] args) { Application.launch(args); }
    
      private Scene mainScene;
      private Scene optionsScene;
      private Stage stage;
    
      @Override public void start(Stage stage) {
        this.stage = stage;
        mainScene    = createMainScene();
        optionsScene = createOptionsScene();
    
        stage.setScene(mainScene);
        stage.show();
      }
    
      private Scene createMainScene() {
        VBox layout = new VBox(10);
        layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
        layout.getChildren().setAll(
          LabelBuilder.create()
            .text("Room Reservation System")
            .style("-fx-font-weight: bold;") 
            .build(),
          HBoxBuilder.create()
            .spacing(5)
            .children(
              new Label("First Name:"),
              new TextField("Peter")
             )
            .build(),
          HBoxBuilder.create()
            .spacing(5)
            .children(
              new Label("Last Name:"),
              new TextField("Parker")
             )
            .build(),
          new Label("Property:"),
          ChoiceBoxBuilder.<String>create()
            .items(FXCollections.observableArrayList(
              "The Waldorf-Astoria", 
              "The Plaza", 
              "The Algonquin Hotel"
            ))
            .build(),
          ButtonBuilder.create()
            .text("Reservation Options  >>")
            .onAction(new EventHandler<ActionEvent>() {
              @Override public void handle(ActionEvent t) {
                stage.setScene(optionsScene);
              }
            })
            .build(),
          ButtonBuilder.create()
            .text("Reserve")
            .defaultButton(true)
            .onAction(new EventHandler<ActionEvent>() {
              @Override public void handle(ActionEvent t) {
                stage.hide();
              }
            })
            .build()
        );
    
        return new Scene(layout);
      }
    
      private Scene createOptionsScene() {
        VBox layout = new VBox(10);
        layout.setStyle("-fx-background-color: azure; -fx-padding: 10;");
        layout.getChildren().setAll(
          new CheckBox("Breakfast"),
          new Label("Paper:"),
          ChoiceBoxBuilder.<String>create()
            .items(FXCollections.observableArrayList(
              "New York Times", 
              "Wall Street Journal", 
              "The Daily Bugle"
            ))
            .build(),
          ButtonBuilder.create()
            .text("Confirm Options")
            .defaultButton(true)
            .onAction(new EventHandler<ActionEvent>() {
              @Override public void handle(ActionEvent t) {
                stage.setScene(mainScene);
              }
            })
            .build()
        );
    
        return new Scene(layout);
      }
    }