如何在javafx中传递参数到辅助窗口?是否有与相应控制器通信的方法?

例如: 用户从TableView中选择一个客户,然后打开一个新窗口,显示该客户的信息。

Stage newStage = new Stage();
try 
{
    AnchorPane page = (AnchorPane) FXMLLoader.load(HectorGestion.class.getResource(fxmlResource));
    Scene scene = new Scene(page);
    newStage.setScene(scene);
    newStage.setTitle(windowTitle);
    newStage.setResizable(isResizable);
    if(showRightAway) 
    {
        newStage.show();
    }
}

newStage将是新窗口。问题是,我找不到一种方法告诉控制器在哪里寻找客户的信息(通过传递id作为参数)。

什么好主意吗?


当前回答

是的,你可以。 你需要添加第一个控制器:

YourController controller = loader.getController();     
controller.setclient(client);

然后在第二个语句中声明一个客户端,然后在控制器的底部:

public void setclien(Client c) {
    this.client = c;
}

其他回答

使用MVC

这个回答主要集中在将参数从调用类传递给控制器的直接调用上。

如果相反,你想要解耦调用者和控制器,并使用一个更通用的架构,包括一个具有可设置和可侦听属性的模型类来实现控制器之间的通信,请参阅以下基本概述:

使用JavaFx应用MVC

推荐的方法

这个答案列举了向FXML控制器传递参数的不同机制。

对于小型应用程序,我强烈建议直接将参数从调用者传递给控制器——这很简单,直接,不需要额外的框架。

对于更大、更复杂的应用程序,如果您想在应用程序中使用依赖注入或事件总线机制,那么值得研究一下。

直接从调用者向控制器传递参数

通过从FXML加载器实例检索控制器并调用控制器上的方法以使用所需的数据值初始化控制器,将自定义数据传递给FXML控制器。

类似于下面的代码:

public Stage showCustomerDialog(Customer customer) {
  FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
      "customerDialog.fxml"
    )
  );

  Stage stage = new Stage(StageStyle.DECORATED);
  stage.setScene(
    new Scene(loader.load())
  );

  CustomerDialogController controller = loader.getController();
  controller.initData(customer);

  stage.show();

  return stage;
}

...

class CustomerDialogController {
  @FXML private Label customerName;
  void initialize() {}
  void initData(Customer customer) {
    customerName.setText(customer.getName());
  }
}

一个新的FXMLLoader被构造,如示例代码所示,即new FXMLLoader(location)。该位置是一个URL,您可以通过以下方式从FXML资源生成这样一个URL:

new FXMLLoader(getClass().getResource("sample.fxml"));

注意不要在FXMLLoader上使用静态加载函数,否则您将无法从加载器实例中获得控制器。

FXMLLoader实例本身从来不知道任何关于域对象的信息。你不直接传递应用程序特定的域对象到FXMLLoader构造函数,而是:

在指定位置根据fxml标记构造一个FXMLLoader 从FXMLLoader实例获取一个控制器。 调用检索到的控制器上的方法,为控制器提供对域对象的引用。

这篇博客(由另一位作者撰写)提供了一个类似的替代例子。

在FXMLLoader上设置控制器

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
        "customerDialog.fxml"
    )
);
loader.setController(dialogController);

Pane mainPane = loader.load();

你可以在代码中构造一个新的控制器,把你想从调用者那里得到的任何参数传递给控制器构造函数。一旦构建了控制器,就可以在调用load()实例方法之前在FXMLLoader实例上设置它。

要在加载器上设置控制器(在JavaFX 2.x中),你不能在你的fxml文件中定义fx:controller属性。

由于FXML中fx:controller定义的限制,我个人更喜欢从FXMLLoader中获取控制器,而不是将控制器设置到FXMLLoader中。

让控制器从外部静态方法检索参数

Sergey在Controller.java文件中对Javafx 2.0 How-to Application.getParameters()的回答举例说明了这种方法。

使用依赖注入

FXMLLoader支持依赖注入系统,如Guice, Spring或Java EE CDI,允许你在FXMLLoader上设置自定义控制器工厂。这提供了一个回调,您可以使用该回调创建控制器实例,其中包含由各自的依赖注入系统注入的依赖值。

下面给出了一个JavaFX应用程序和控制器依赖注入Spring的示例:

在JavaFX中添加Spring依赖注入(JPA Repo, Service)

加力是一种非常好的、干净的依赖注入方法。Fx框架与示例air-hacks应用程序使用它。加力燃烧室。fx依赖于JEE6 javax。注入以执行依赖项注入。

使用事件总线

Greg Brown是FXML规范的最初创建者和实现者,他经常建议考虑使用事件总线,例如Guava EventBus,用于FXML实例化控制器和其他应用程序逻辑之间的通信。

EventBus是一个简单但功能强大的带有注释的发布/订阅API,它允许pojo在JVM中的任何地方相互通信,而不必相互引用。

后续的问答

关于第一种方法,你为什么返回舞台?这个方法也可以是空的,因为你已经给出了show()命令;在返回阶段之前;。如何通过返回Stage来计划使用

It is a functional solution to a problem. A stage is returned from the showCustomerDialog function so that a reference to it can be stored by an external class which may wish to do something, such as hide the stage based on a button click in the main window, at a later time. An alternate, object-oriented solution could encapsulate the functionality and stage reference inside a CustomerDialog object or have a CustomerDialog extend Stage. A full example for an object-oriented interface to a custom dialog encapsulating FXML, controller and model data is beyond the scope of this answer, but may make a worthwhile blog post for anybody inclined to create one.


StackOverflow用户@ zim提供的附加信息

Spring引导依赖注入示例

关于如何做到“Spring Boot Way”的问题,有一个关于JavaFX 2的讨论,我在附件中回答了这个问题。 该方法仍然有效,并在2016年3月Spring Boot v1.3.3.RELEASE上进行了测试: https://stackoverflow.com/a/36310391/1281217


有时,你可能想要将结果返回给调用者,在这种情况下,你可以检查出相关问题的答案:

JavaFX FXML从控制器A传递到控制器B并返回的参数

我意识到这是一个非常老的帖子,已经有了一些很好的答案, 但我想做一个简单的MCVE来演示这样一种方法,并允许新程序员快速了解实际的概念。

在这个例子中,我们将使用5个文件:

java -简单地用于启动应用程序并调用第一个控制器。 java -第一个FXML布局的控制器。 java -第二个FXML布局的控制器。 Layout1。fxml -第一个场景的fxml布局。 Layout2。fxml -第二个场景的fxml布局。

所有文件都完整地列在这篇文章的底部。

目标:演示将值从Controller1传递到Controller2,反之亦然。

程序流程:

第一个场景包含一个TextField、一个Button和一个Label。单击按钮时,将加载并显示第二个窗口,包括在TextField中输入的文本。 在第二个场景中,还有一个TextField、一个Button和一个Label。Label将显示第一个场景中TextField中输入的文本。 在第二个场景的TextField中输入文本并单击它的Button后,第一个场景的Label将更新以显示输入的文本。

这是一个非常简单的演示,当然可以进行一些改进,但应该使概念非常清楚。

代码本身也注释了发生了什么以及如何发生的一些细节。

的代码

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Create the first controller, which loads Layout1.fxml within its own constructor
        Controller1 controller1 = new Controller1();

        // Show the new stage
        controller1.showStage();

    }
}

Controller1.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller1 {

    // Holds this controller's Stage
    private final Stage thisStage;

    // Define the nodes from the Layout1.fxml file. This allows them to be referenced within the controller
    @FXML
    private TextField txtToSecondController;
    @FXML
    private Button btnOpenLayout2;
    @FXML
    private Label lblFromController2;

    public Controller1() {

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout1.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout1");

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

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    /**
     * The initialize() method allows you set setup your scene, adding actions, configuring nodes, etc.
     */
    @FXML
    private void initialize() {

        // Add an action for the "Open Layout2" button
        btnOpenLayout2.setOnAction(event -> openLayout2());
    }

    /**
     * Performs the action of loading and showing Layout2
     */
    private void openLayout2() {

        // Create the second controller, which loads its own FXML file. We pass a reference to this controller
        // using the keyword [this]; that allows the second controller to access the methods contained in here.
        Controller2 controller2 = new Controller2(this);

        // Show the new stage/window
        controller2.showStage();

    }

    /**
     * Returns the text entered into txtToSecondController. This allows other controllers/classes to view that data.
     */
    public String getEnteredText() {
        return txtToSecondController.getText();
    }

    /**
     * Allows other controllers to set the text of this layout's Label
     */
    public void setTextFromController2(String text) {
        lblFromController2.setText(text);
    }
}

Controller2.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller2 {

    // Holds this controller's Stage
    private Stage thisStage;

    // Will hold a reference to the first controller, allowing us to access the methods found there.
    private final Controller1 controller1;

    // Add references to the controls in Layout2.fxml
    @FXML
    private Label lblFromController1;
    @FXML
    private TextField txtToFirstController;
    @FXML
    private Button btnSetLayout1Text;

    public Controller2(Controller1 controller1) {
        // We received the first controller, now let's make it usable throughout this controller.
        this.controller1 = controller1;

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout2.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout2");

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

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    @FXML
    private void initialize() {

        // Set the label to whatever the text entered on Layout1 is
        lblFromController1.setText(controller1.getEnteredText());

        // Set the action for the button
        btnSetLayout1Text.setOnAction(event -> setTextOnLayout1());
    }

    /**
     * Calls the "setTextFromController2()" method on the first controller to update its Label
     */
    private void setTextOnLayout1() {
        controller1.setTextFromController2(txtToFirstController.getText());
    }

}

Layout1.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="This is Layout1!"/>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToSecondController"/>
            <Button fx:id="btnOpenLayout2" mnemonicParsing="false" text="Open Layout2"/>
        </HBox>
        <VBox alignment="CENTER">
            <Label text="Text From Controller2:"/>
            <Label fx:id="lblFromController2" text="Nothing Yet!"/>
        </VBox>
    </VBox>
</AnchorPane>

Layout2.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="Welcome to Layout 2!"/>
        <VBox alignment="CENTER">
            <Label text="Text From Controller1:"/>
            <Label fx:id="lblFromController1" text="Nothing Yet!"/>
        </VBox>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToFirstController"/>
            <Button fx:id="btnSetLayout1Text" mnemonicParsing="false" text="Set Text on Layout1"/>
        </HBox>
    </VBox>
</AnchorPane>

你可以决定使用一个公共可观察对象列表来存储公共数据,或者只是创建一个公共setter方法来存储数据并从相应的控制器中检索

是的,你可以。 你需要添加第一个控制器:

YourController controller = loader.getController();     
controller.setclient(client);

然后在第二个语句中声明一个客户端,然后在控制器的底部:

public void setclien(Client c) {
    this.client = c;
}

node类有一对方法 setUserData(对象) 而且 对象getUserData ()

您可以使用它将您的信息添加到Node。

因此,你可以调用page.setUserData(info);

如果设置了info,控制器可以检查。此外,如果需要,还可以使用ObjectProperty进行前后数据传输。

请看下面的文档: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html 在第一个版本中,handleButtonAction()被标记为@FXML,以允许控制器文档中定义的标记调用它。在第二个示例中,button字段被注释以允许加载器设置它的值。initialize()方法也有类似的注释。”

因此,您需要将控制器与节点关联起来,并将用户数据设置为节点。