使用 fx:include 时的自定义控制器工厂

Ond*_*zel 3 java user-interface javafx fxml fxmlloader

我使用的是 JavaFX 15.0.1 版。我想通过向其中注入几个 FXML 文件来制作更复杂的场景,如下所示:

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

<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>

<AnchorPane fx:controller="MainFxmlController">
   <children>
      <VBox>
         <children>
            <fx:include fx:id="topMenu" source="top_menu.fxml" />
            <fx:include fx:id="navigation" source="navigation.fxml" />
            <fx:include fx:id="statusBar" source="status_bar.fxml" />
         </children>
      </VBox>
   </children>
</AnchorPane>
Run Code Online (Sandbox Code Playgroud)

在这里,我发现包含的 FXML 的控制器会自动加载并注入主控制器中名为 <value of fx:id>Controller 的 @FXML 注释字段(在本例中MainFxmlController)。

我的问题是:在这种情况下,我如何使用自己的控制器工厂来实例化相应的控制器类?我需要在构造函数中为控制器提供一些依赖项。

Jam*_*s_D 6

包含的 FXML 和封闭的 FXML 将使用相同的控制器工厂;所以你的控制器工厂可以测试哪个控制器类被传递给回调方法,创建适当的对象,并将依赖项传递给它。

像这样的东西:

// application model class:
DataModel model = new DataModel();

FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(controllerType -> {

    if (controllerType == MainController.class) {
        return new MainController(model);
    }

    if (controllerType == TopMenuController.class) {
        return new TopMenuController(model);
    }

    if (controllerType == NavigationController.class) {
        return new NavigationController(model);
    }

    if (controllerType == StatusBarController.class) {
        return new StatusBarController(model);
    }

    return null ; // or throw an unchecked exception
});

Parent mainRoot = loader.load();
Run Code Online (Sandbox Code Playgroud)

如果你喜欢(或需要更多的通用性),你可以使用反射:

loader.setControllerFactory(controllerType -> {

    try {
        for (Constructor<?> c : controllerType.getConstructors()) {
            if (c.getParameterCount() == 1 
                && c.getParameterTypes()[0] == DataModel.class) {

                return c.newInstance(model);
            }
        }
        // If we got here, there's no constructor taking a model,
        // so try to use the default constructor:
        return controllerType.getConstructor().newInstance();
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
        throw new RuntimeException(e);
    }
});
Run Code Online (Sandbox Code Playgroud)

  • @OndřejKozel 不要。更改模型,并让另一个控制器更新其视图以响应模型中的更改。请参阅 /sf/answers/2264033971/ 一般来说,如果一个控制器需要访问另一个控制器,那么这是一个糟糕的设计。 (3认同)