小编典典

传递参数 JavaFX FXML

all

如何将参数传递给 javafx 中的辅助窗口?有没有办法与相应的控制器进行通信?

例如:用户从 a 中选择一个客户,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 作为参数传递)。

有任何想法吗?


阅读 80

收藏
2022-06-16

共1个答案

小编典典

使用 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 构造函数,而是:

  1. 在指定位置基于 fxml 标记构造 FXMLLoader
  2. 从 FXMLLoader 实例中获取控制器。
  3. 在检索到的控制器上调用方法以向控制器提供对域对象的引用。

此博客(由另一位作者撰写)提供了一个替代但类似的示例

在 FXMLLoader 上设置控制器

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

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

Pane mainPane = loader.load();

您可以在代码中构造一个新的控制器,将您想要的任何参数从调用者传递到控制器构造函数中。构造控制器后,您可以在调用 实例 方法 之前将其设置在
FXMLLoader 实例上。
load() __

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

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

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

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

使用依赖注入

FXMLLoader 通过允许您在 FXMLLoader 上设置自定义控制器工厂来支持 Guice、Spring 或 Java EE CDI
等依赖注入系统。这提供了一个回调,您可以使用该回调创建具有由相应依赖注入系统注入的依赖值的控制器实例。

答案中提供了使用 Spring 进行 JavaFX 应用程序和控制器依赖注入的示例:

afterburner.fx 框架与使用它的示例air-
hacks 应用程序
举例说明了一种非常好的、干净的依赖注入方法。afterburner.fx 依赖于 JEE6
javax.inject来执行依赖注入。

使用事件总线

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

EventBus 是一个简单但功能强大的带有注释的发布/订阅 API,它允许 POJO 在 JVM 中的任何位置相互通信,而无需相互引用。

后续问答

在第一种方法上,为什么要返回 Stage?该方法也可以是无效的,因为您已经给出了命令 show(); 就在返回阶段之前;。您如何通过返回 Stage
来计划使用情况

它是解决问题的功能性解决方案。从showCustomerDialog函数返回一个阶段,以便外部类可以存储对它的引用,该类可能希望稍后执行某些操作,例如基于在主窗口中单击按钮来隐藏阶段。另一种面向对象的解决方案可以将功能和阶段引用封装在
CustomerDialog 对象中,或者让 CustomerDialog 扩展阶段。封装
FXML、控制器和模型数据的自定义对话框的面向对象接口的完整示例超出了此答案的范围,但对于任何倾向于创建的人来说,这可能是一篇有价值的博文。

Spring Boot 依赖注入示例

关于如何做到这一点的问题“Spring Boot Way”,有一个关于 JavaFX 2 的讨论,我在附加的永久链接中回答了这个问题。该方法仍然有效,并于
2016 年 3 月在 Spring Boot v1.3.3.RELEASE 上进行了测试

2022-06-16