Home > Net >  JavaFX: Multiple 'views', 'pages' or scenes - how to approach?
JavaFX: Multiple 'views', 'pages' or scenes - how to approach?

Time:12-29

I am trying to build a dummy webshop with JavaFX. I deliberately do not use fxml, scenebuilder, maven or any other build tool. Just plain JavaFX, in order to really get to understand the basics. However, I ran into a problem creating and navigating different 'pages'.

I have tried various creative solutions, like this one (is posting links allowed?), but none fully work for me, as I want every 'page', 'view' or scene in a seperate java class file, in order to keep everything structured and orderly.

I figured I'd make a Borderpane as a parent layout for every page

abstract class WindowBase extends BorderPane {

    public abstract BorderPane render(App app);

    public WindowBase() {
    
        Label labelTop = new Label("Top box");

        HBox topBox = new HBox();
        topBox.setStyle("-fx-background-color: red;");
        topBox.getChildren().addAll(labelTop);

        Label labelLeft = new Label("Left box");

        VBox leftBox = new VBox();
        leftBox.setStyle("-fx-background-color: green;");
        leftBox.getChildren().addAll(labelLeft);

        Label labelRight = new Label("Right box");

        VBox rightBox = new VBox();
        rightBox.setStyle("-fx-background-color: blue;");
        rightBox.getChildren().addAll(labelRight);

        Label labelBottom = new Label("Bottom box");

        HBox bottomBox = new HBox();
        bottomBox.setStyle("-fx-background-color: yellow;");
        bottomBox.getChildren().addAll(labelBottom);

        this.setTop(topBox);
        this.setLeft(leftBox);
        this.setRight(rightBox);
        this.setBottom(bottomBox);
    }
}

and a child, the home page

public class Homepage extends WindowBase {

    public BorderPane render(App app) {

        Button button = new Button("Go to shopping cart");
        button.setOnAction((event) -> app.toShoppingCart());

        StackPane centerPane = new StackPane();
        centerPane.getChildren().add(button);
        this.setCenter(centerPane);

        return this;
    }
}

and lastly my App.java that runs everything

public class App extends Application{

    private WindowBase view;

    public void start(Stage stage) throws Exception {

        view = new Homepage();

        stage.setScene(new Scene(view.render(this)));
        stage.setFullScreen(true);
        stage.show();

    }

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

    public void toHomepage() {
        this.view = new Homepage();
    }

    public void toShoppingCart() {
        this.view = new ShoppingCart();
    }
}

I understand that I can't pass this (App) as an argument to view.render(), use the parameter within the method render and expect to be able to manipulate it, because it only creates a new instance of App as soon as it gets there. However, I see no other way either.

I tried placing the navigation buttons in the App class, in order to be able to manipulate view, but then I cannot call on the buttons from the subsequent views.

There must be a way to achieve what I want without writing the complete GUI in one file, right? Should I make my view static in stead, is that it?

Instead of BorderPanes I am of course also okay with using Scenes, whatever works.

CodePudding user response:

I have figured out exactly the solution that I wanted. Posting it here for whoever encounters the same situation.

Class WindowBase

public class WindowBase {

    public BorderPane getMainPane() {
    
        Label labelTop = new Label("Top box");
    
        HBox topBox = new HBox();
        topBox.setStyle("-fx-background-color: red;");
        topBox.getChildren().addAll(labelTop);

        Label labelLeft = new Label("Left box");

        VBox leftBox = new VBox();
        leftBox.setStyle("-fx-background-color: green;");
        leftBox.getChildren().addAll(labelLeft);

        Label labelRight = new Label("Right box");

        VBox rightBox = new VBox();
        rightBox.setStyle("-fx-background-color: blue;");
        rightBox.getChildren().addAll(labelRight);

        Label labelBottom = new Label("Bottom box");

        HBox bottomBox = new HBox();
        bottomBox.setStyle("-fx-background-color: yellow;");
        bottomBox.getChildren().addAll(labelBottom);

        BorderPane borderPane = new BorderPane();

        borderPane.setTop(topBox);
        borderPane.setLeft(leftBox);
        borderPane.setRight(rightBox);
        borderPane.setBottom(bottomBox);

        return borderPane;
    }
}

Class Homepage

public class Homepage extends WindowBase {

    public BorderPane render(Button toShoppingCart) {

        Label label = new Label("This is the homepage.");
        label.setStyle(
                "-fx-font: normal bold 30px 'elephant'; -fx-text-fill: black; -fx-background-color: red;");

        StackPane stackPane = new StackPane();
        stackPane.getChildren().addAll(label);

        BorderPane mainPane = getMainPane();
        VBox leftBox = (VBox) mainPane.getLeft();
        leftBox.getChildren().add(toShoppingCart);

        //I do not understand why this works. I abstracted leftBox from mainPane, then added shoppingCart, but never added the abstracted
        //  leftBox back to the mainPane before returning it. This should not work, but it does. leftBox.getChildren().add(toShoppingCart)
        //  should have no effect.
        //However, mainPane.getChildren().add(leftBox) throws an IllegalArgumentException about duplicate components, which is to be
        //  expected if the leftBox is already automatically added back to the mainPane.

        mainPane.setCenter(stackPane);

        return mainPane;
    }
}

Class ShoppingCart

public class ShoppingCart extends WindowBase {

    public ShoppingCart() {
        super();
    }

    public BorderPane render(Button toHomepage) {
        
        Label label = new Label("This is the shopping cart.");
        label.setStyle(
            "-fx-font: normal bold 30px 'elephant'; -fx-text-fill: black; -fx-background-color: red;");
        
        StackPane centerPane = new StackPane();
        centerPane.getChildren().add(label);
    
        BorderPane mainPane = getMainPane();
        VBox leftBox = (VBox) mainPane.getLeft();
        leftBox.getChildren().add(toHomepage);

        mainPane.setCenter(centerPane);

        return mainPane;
    }
}

Class App

public class App extends Application{

    Scene scene;

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

        Homepage homePane = new Homepage();
        Button toHomepage = new Button("Back to home page");

        ShoppingCart shoppingPane = new ShoppingCart();
        Button toShoppingCart = new Button("To shopping cart");

        toHomepage.setOnAction(e -> scene.setRoot(homePane.render(toShoppingCart)));
        toShoppingCart.setOnAction(e -> scene.setRoot(shoppingPane.render(toHomepage)));

        scene = new Scene(homePane.render(toShoppingCart), 600, 400);

        stage.setScene(scene);
        stage.setFullScreen(true);
        stage.show();

    }

    public static void main(String[] args) throws Exception {
        
        launch(args);
    }
}
  • Related