0

I've realised only through clearing up code that I thought I could clear up and had cleared from some of my Controller classes where my issue is. To word my question better and provide more clarity to the concept of prioritisation I'll give a brief explanation followed by some code. Imagine an interface with a side of the screen being consistent and permanent to the interface where you have Buttons for menu-ing where you essentially move between tabs changing the rest of the Stage passing fxml files to the Pane associated.

Reading examples and questions I understood this to be viable and using the framework from this link https://gist.github.com/jewelsea/6460130 seeing a header remain consistent while switching Scene between vista1.fxml and vista2.fxml, my understanding was that if the Header remains from main.fxml as part of the scene that the Stage is therefore using either both main.fxml and vista1.fxml or main.fxml and vista2.fxml concurrently and thus both associated controllers exist and will have access to corresponding methods according to the Pane interacted with.

That is clearly not the case though and the consistent Pane in this case main.fxml does not have its controller since when the other is loaded and creates its controller it replaces or clears the other controller in some form or another I now presume is the case. I thought I could avoid code duplication by using separate Panes with the consistency of the menu bar but also being able to use its buttons.

Some code as an example: Main.java

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.io.IOException;

public class Main extends Application {

    @Override
    public void start(Stage stage) throws Exception{
        stage.setTitle("Vista Viewer");

        stage.setScene(createScene(loadMainPane()));

        stage.show();
    }

    private Pane loadMainPane() throws IOException {
        FXMLLoader loader = new FXMLLoader();

        Pane mainPane = (Pane) loader.load(getClass().getResourceAsStream(
                        VistaNavigator.MAIN));

        MainController mainController = loader.getController();

        VistaNavigator.setMainController(mainController);
        VistaNavigator.loadVista(VistaNavigator.VISTA_1);

        return mainPane;
    }

    private Scene createScene(Pane mainPane) {
        Scene scene = new Scene(mainPane);

        return scene;
    }

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

VistaNavigator.java

package sample;

import javafx.fxml.FXMLLoader;
import java.io.IOException;

public class VistaNavigator {

    public static final String MAIN    = "main.fxml";
    public static final String VISTA_1 = "vista1.fxml";
    public static final String VISTA_2 = "vista2.fxml";
    public static final String OTHER_MENU_OPTION = "otherMenu.fxml";

    private static MainController mainController;

    public static void setMainController(MainController mainController) {
        VistaNavigator.mainController = mainController;
    }

    public static void loadVista(String fxml) {
        try {
            mainController.setVista(
                    FXMLLoader.load(
                            VistaNavigator.class.getResource(
                                    fxml
                            )
                    )
            );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

MainController.java

package sample;

import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;

public class MainController {

    @FXML
    private StackPane vistaHolder;

    public void setVista(Node node) {
        vistaHolder.getChildren().setAll(node);
    }

    public void homebtn() throws Exception{
        VistaNavigator.loadVista(VistaNavigator.VISTA_1);
    }

    public void otherMenuOption() throws Exception{
        VistaNavigator.loadVista(VistaNavigator.OTHER_MENU_OPTION);
    }
}

main.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="200.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml" fx:controller="sample.MainController">
    <children>
        <VBox prefWidth="155.0">
            <children>
                <Label fx:id="headerLabel" maxWidth="120.0" text="Header" />
            </children>
        </VBox>
        <Pane layoutY="40.0" prefHeight="200.0" prefWidth="120.0" style="-fx-background-color: #091D34;">
            <children>
                <Button fx:id="button1" layoutY="60.0" mnemonicParsing="false" onAction="#homebtn" prefHeight="44.0" prefWidth="120.0" style="-fx-background-color: #133863;" text="Home" textFill="WHITE">
                    <font>
                        <Font name="System Bold" size="12.0" />
                    </font>
                </Button>
                <Button fx:id="button2" layoutY="110.0" mnemonicParsing="false" onAction="#otherMenuOption" prefHeight="44.0" prefWidth="120.0" style="-fx-background-color: #133863;" text="OtherMenuOption" textFill="WHITE">
                    <font>
                        <Font name="System Bold" size="12.0" />
                    </font>
                </Button>
            </children>
        </Pane>
        <StackPane fx:id="vistaHolder" VBox.vgrow="ALWAYS" />
    </children>
</AnchorPane>

otherMenu.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<StackPane fx:id="otherMenuOption" xmlns:fx="http://javafx.com/fxml" fx:controller="sample.Vista1Controller">
    <children>
        <Label fx:id="label" layoutX="135.0" layoutY="57.0" maxWidth="120.0" text="other program stuff" VBox.vgrow="NEVER" />
    </children>
</StackPane>

Vista1Controller.java

package sample;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;

public class Vista1Controller {

    @FXML
    void nextPane(ActionEvent event) {
        VistaNavigator.loadVista(VistaNavigator.VISTA_2);
    }
}

vista1.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<AnchorPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="sample.Vista1Controller" prefHeight="400.0" prefWidth="600.0">
    <children>
        <Pane fx:id="Vista1" layoutX="155.0" layoutY="-1.0" prefHeight="430.0" prefWidth="574.0" style="-fx-background-color: transparent">
            <Button fx:id="btn" layoutX="135.0" layoutY="57.0" mnemonicParsing="false" onAction="#nextPane" prefHeight="149.0" prefWidth="318.0" style="-fx-background-color: #133863;" text="Open Vista2" textFill="#ffffff">
                <font>
                    <Font name="Britannic Bold" size="20.0" />
                </font>
            </Button>
        </Pane>
    </children>
</AnchorPane>

Vista2Controller.java

package sample;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;

public class Vista2Controller {

    @FXML
    void previousPane(ActionEvent event) {
        VistaNavigator.loadVista(VistaNavigator.VISTA_1);
    }
}

vista2.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<AnchorPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="sample.Vista2Controller" prefHeight="400.0" prefWidth="600.0">
    <children>
        <Pane fx:id="Vista2" layoutX="155.0" layoutY="-1.0" prefHeight="430.0" prefWidth="574.0" style="-fx-background-color: transparent">
            <Button fx:id="btn" layoutX="135.0" layoutY="57.0" mnemonicParsing="false" onAction="#previousPane" prefHeight="149.0" prefWidth="318.0" style="-fx-background-color: #133863;" text="Open Vista1" textFill="#ffffff">
                <font>
                    <Font name="Britannic Bold" size="20.0" />
                </font>
            </Button>
        </Pane>
    </children>
</AnchorPane>

Is there a good way to maintain this design and prevent code duplication within the specific controllers and would it be through inheritance or passing parameters, I'm not sure what this is comparable to nor whether it's possible?

  • 1
    [mcve] please .. – kleopatra Apr 29 '21 at 15:51
  • Your question doesn't make sense. When you load an FXML file, the `FXMLLoader` creates a controller, and creates instances of everything in the hierarchy defined by the FXML file (the controls and panes, etc). If the use, for example, presses one of the buttons created from the FXML file, then the corresponding method in the controller that was created is executed. If you load the FXML file again, another controller is created, and another hierarchy corresponding to the FXML elements. There is no "prioritization". There's just an association between controls and controllers. – James_D Apr 29 '21 at 17:21
  • Ok so I've tried to create a minimal reproducible example and minimise the files while maintaining the same issue. I can see the issue through comparing files but have found it hard to create a minimal example. I might be able to quickly drum something up since as I originally theorised its not so much the content within than the content not within that is relevant to my question. will edit the question with some code asap. – Michael Evans Apr 29 '21 at 17:46
  • @kleopatra produced an example – Michael Evans Apr 29 '21 at 18:30
  • @James_D hopefully my question makes more sense now? since 2 FXML files are used and loaded separately I ought to have known the latest loaded one will have a controller created and the previous one won't have a controller, I gather that this is likely because everytime you change your scene using an FXML file you don't want objects and methods still taking up space or interfering. I was focused on the practicality of removing duplicate code. – Michael Evans Apr 29 '21 at 18:36
  • *"The previous one won't have a controller"*. Not really. The previous one still has a controller. If you are no longer displaying the UI loaded from the previous FXML, and you don't have a reference to its controller, then the controller will be garbage collected at some point in the usual way. But if you are still displaying the previous FXML, then it still has a controller. (And even if not, then the controller still exists until it is garbage collected.) – James_D Apr 29 '21 at 18:50
  • The buttons in the left pane don't work because the `StackPane` is over the top of them and obscuring them. It has nothing to do with the controllers, etc. Use proper layouts instead of using `Pane`s and `AnchorPane`s and trying to manage the layout yourself. – James_D Apr 29 '21 at 19:09

0 Answers0