将数据传递给 Javafx 中的另一个控制器

Passing data to another controller in Javafx

我在 Java 中开发我的项目还是个新手,你能指出我在将字符串传递给另一个控制器时做错了什么吗?以下是我的解决方案。我正在尝试将字符串 purchaseCode 的值从我的第一个控制器 (DashboardController.java) 传递到另一个控制器,以便在查询中使用它或只是将其设置为文本。但是当我将它调用给我的另一个控制器时,我得到的结果是 null(InfoSalesController.java).

DashboardController.java

...
SalesController sales = getTableView().getItems().get(getIndex());
String purchaseCode = sales.getPurchaseCol();
Hyperlink link = new Hyperlink(purchaseCode);
                             
link.setOnAction(event->{
  try{
    Stage stage = new Stage();
    FXMLLoader loader = new 
    FXMLLoader(getClass().getResource("/store/sales/infoSales.fxml"));
    Parent pane = (Parent) loader.load();
    InfoSalesController is = loader.getController();
    is.setPurchaseSales(purchaseCode);
    stage.setScene(new Scene(pane));
    stage.setResizable(false);
    stage.initModality(Modality.APPLICATION_MODAL);
    stage.show();
    }catch(IOException e){
      e.printStackTrace();
    }
});
setGraphic(link);
...

InfoSalesController.java

public String getPurchaseSales() {
    return purchaseSales;
}

public void setPurchaseSales(String purchaseSales) {
    this.purchaseSales = purchaseSales;
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    purchaseTxt.setText(purchaseSales);
}

这个问题实际上是重复的:

  • Passing Parameters JavaFX FXML

但是这个问题提出了一个有趣的附带问题:“Platform.runLater() 如何帮助程序工作?”,我将 post 在这里作为解释回答。

首先,让我们看一下传递参数答案中的代码并解释其工作原理:

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());
  }
}

这里的关键区别是 CustomerDialogController 中的 initialize() 语句是空的,并且添加了一个单独的 initData() 函数。 initialize() 语句在加载 FXML 时由 FXMLLoader 隐式调用。那时,初始化视图实际需要的其余数据(客户数据)不可用,因为它尚未传递给新控制器。 initData(Customer customer) 调用是在加载后显式调用的,并且包含客户数据作为参数,因此它可用于初始化视图。

现在让我们看看您问题中的代码以及 Platform.runLater 对它做了什么。

您的调用控制器中有此代码:

FXMLLoader loader = new 
FXMLLoader(getClass().getResource("/store/sales/infoSales.fxml"));
Parent pane = (Parent) loader.load();
InfoSalesController is = loader.getController();
is.setPurchaseSales(purchaseCode);

在您的接收控制器中:

public void setPurchaseSales(String purchaseSales) {
    this.purchaseSales = purchaseSales;
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    purchaseTxt.setText(purchaseSales);
}

这不起作用,因为调用初始化语句时 purchaseTxt 为空。

但是如果你执行以下操作,则 purchaseTxt 不为空:

@Override
public void initialize(URL url, ResourceBundle rb) {
    Platform.runLater(() -> {
        purchaseTxt.setText(purchaseSales);
    });  
}

initialize()的内容包裹在Platform.runLater()中的作用是将初始化函数的处理延迟到将来某个未定义的点。

很可能会在下一个场景脉冲时执行。 pulse 基于 JavaFX 用于处理动画和布局更改以及向应用程序代码发送事件和回调的内部计时器。

例如,Platform.runLater() 代码可以在 1/60 秒后执行,在:

之后
  1. 您申请中针对当前脉冲的所有后续语句均已执行并且
  2. 控制权已移交给执行动画、渲染和布局过程的 JavaFX 应用程序框架并且
  3. 控制权已返回到您的应用程序代码以执行下一个脉冲的应用程序处理。

因此,Platform.runLater() 实际上使初始化过程成为异步调用。异步逻辑在所有后续代码执行完毕后执行。这意味着您在新控制器中设置应用程序数据的代码将在执行 initialize() Platform.runLater() 块中的异步代码以初始化由新控制器管理的视图元素之前执行。

两种方法都有效。我猜你可以选择你和其他读者最清楚的那个。就个人而言,我不会在这种情况下使用 Platform.runLater()