JavaFX - Many Static FXML Controllers

2020-02-01 08:10发布

A JavaFX application exists and the application is starting from the Main.class file which extends Application:

  public class Main extends Application {

    /**
     * Keep a reference to the main Stage
     */
    public static Stage                 stage;
    /**
     * MainScene Controller
     */
    public static MainScene             mainSceneController;
    /**
     * The Capture Window of the application
     */
    public static CaptureWindow         captureWindowController;
    /**
     * Settings Scene Controller
     */
    public static SettingsController    settingsController;

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

        stage = primary;
        ..........

        // CaptureWindow
        FXMLLoader loader1 = new FXMLLoader(getClass().getResource("/fxml/CaptureWindow.fxml"));
        loader1.load();
        captureWindowController = loader1.getController();

        // MainScene
        mainSceneController = new MainScene();

        .........   
    }

  }

Description

As you can see above I have 3 FXMLControllers(one is reusable[extends StackPane],others not).I have declared all of them static cause i want to access variables from one FXMLController from the other FXMLControllers.I use this strategy every time I use JavaFX and I don't think is good...

How I can change the code below so I can access variables or methods of one FXMLController from other FXMLController? Is that possible without using static keyword?

Consider that the Controllers are represented from different classes which can be in different packages.


Also before writing this question I had a look Static @FXML variables in FXML Controller

标签: java javafx
5条回答
beautiful°
2楼-- · 2020-02-01 08:41

JavaFx is mostly composed of set of [well designed] tools but unfortunately by itself does not provide a good framework for creating complex UI designs e.g. MVC/MVP patterns, view flows and actions on multiple controllers, So you have to rely on third-party application frameworks for those for example:

In my opinion none of them are widely used or mature enough to be considered a de facto standard but using them is encouraged.


Example Using DataFx

DataFx uses a concept named Flow to associate views sharing a flow of events (and possibly data) among themselves. By using Flow combined with EventSystem you can define and access methods on other controllers and assign custom event listeners to various events associated with JavaFx Nodes in different controllers sharing a flow.

Here is an example from DataFx samples which represents two separate sender and receiver views with distinct FXML files and controllers:

enter image description here

public class EventSystemDemo extends Application {

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

    HBox box = new HBox();
    box.setSpacing(12);
    box.setPadding(new Insets(12));
    box.setFillHeight(true);
    box.setAlignment(Pos.CENTER);

    Flow senderFlow = new Flow(ProducerController.class);
    box.getChildren().add(senderFlow.start());

    Flow receiverFlow = new Flow(ReceiverController.class);
    box.getChildren().add(receiverFlow.start());

    primaryStage.setScene(new Scene(box));
    primaryStage.show();

    }

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

Sender view controller:

@ViewController("producer.fxml")
public class ProducerController {

    @FXML
    @EventTrigger()
    private Button sendButton;

    @FXML
    private TextField textField;

    @EventProducer("test-message")
    private String getMessage() {
        return textField.getText();
    }

}

Receiver view controller:

@ViewController("receiver.fxml")
public class ReceiverController {

    @FXML
    private Label messageLabel;

    @OnEvent("test-message")
    private void onNewChatMessage(Event<String> e) {
        messageLabel.setText(e.getContent());
    }
}

Sender View:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.text.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="12.0" style="-fx-border-color: darkgrey; -fx-border-width: 2;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Label text="Sender">
         <font>
            <Font name="System Bold" size="24.0" />
         </font>
      </Label>
      <TextField fx:id="textField" />
      <Button fx:id="sendButton" mnemonicParsing="false" text="send" />
   </children>
   <padding>
      <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
   </padding>
</VBox>

Receiver View:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.text.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="12.0" style="-fx-border-color: darkgrey; -fx-border-width: 2;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <children>
        <Label text="Receiver">
         <font>
            <Font name="System Bold" size="24.0" />
         </font></Label>
      <Label fx:id="messageLabel" />
    </children>
    <padding>
        <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
    </padding>
</VBox>

For further reading:

http://www.oracle.com/technetwork/java/javafx/community/3rd-party-1844355.html

http://www.guigarage.com/2015/02/quick-overview-datafx-mvc-flow-api/

http://www.guigarage.com/2014/05/datafx-8-0-tutorials/

http://jacpfx.org/2014/11/03/JacpFX_DataFX_a_perfect_match.html

Passing Parameters JavaFX FXML

查看更多
别忘想泡老子
3楼-- · 2020-02-01 08:43

I think you are not supposed to declare @FXML annotated attributes with the static keyword. See this GitHub project on how you should do it. Like that you instantiate a controller only when it is needed, and your app will be stateless.

查看更多
劫难
4楼-- · 2020-02-01 08:48

Actually the answer for this question seems a little bit complicated it has to do with MVC pattern and it's evolution until now.We will use MVP Pattern.

After a long discussion i got a link on this website http://martinfowler.com/eaaDev/uiArchs.html defining the historical evolution of the different patterns used from the old ages of Smalltalk until now.


The actually solution is using Model Viewer Presenter Pattern(MVP) which can be visually described using these images:

enter image description here

enter image description here

For more you can read(http://www.wildcrest.com/Potel/Portfolio/mvp.pdf)


For an example on JavaFX have a look on James_D answer here (Applying MVC With JavaFx)


Last but not least have a look at the comments here:

Finally:

If anything is inaccurate feel free to edit.

查看更多
▲ chillily
5楼-- · 2020-02-01 08:58

You can implement your own controller factory so you would be able of creating your own controllers context.

I have used spring with javaFX, and I have implemented my own controllers factory using spring, in that way you can inject one controller in other (you shouldn't need it as those connections should be on the model)

If you want to try spring with Java fx: How to wire multiple fxml controllers using spring dependency-injection?

查看更多
Anthone
6楼-- · 2020-02-01 09:02

Definitely you should use an Application Framework that will help you to structure your code.

If you want to try JRebirth, just cut your application in 4 parts:

  • MainModel composed by:
    • MainSceneModel
    • CaptureWindowModel
    • SettingsModel

You can access to one from anyone by using getModel(Model.class) or send async message using sendWave(Wave).

MainModel can use innerComponent to tightly link child-model to it.

It's pretty simple to use, if you are interested in I can prototype you a sample app just let me know.

查看更多
登录 后发表回答