JavaFX中如何在没有控制器的情况下将fxml文件包含在另一个fxml文件中

9
我正在使用JavaFX编写一个应用程序。这是一个“多屏幕”应用程序,有一个主菜单,可以在其中切换场景。
我的场景定义在不同的FXML文件中。
因为我尝试使用MVC模式,所以我没有在FXML文件中设置控制器,而是在FXMLloader上使用setController。
一切都运行正常,但我的主菜单和所有onActions的功能都在单独的控制器和单独的FXML文件中。
我已经尝试过使用
<fx:include source="Menubar.fxml"/>

我创建了一个与 fxml 文件相关联的控制器,但当我在 fxml 文件中设置控制器时,无法编译源代码。如何为所包含的 fxml 文件设置控制器?

startpage.fxml 使用以下方式获取其控制器“Startpage”

FXMLLoader loader = new FXMLLoader(getClass().getResource("../fxml/startpage.fxml"));
        loader.setController(new Startpage(m));
        Pane mainPane = loader.load();

startpage.fxml 包含 menubar.fxml,现在如何为 menubar 控件设置控制器?或者如何轻松地在其他每个控制器中包含 menubarController?

1个回答

26

我认为你需要在加载器中使用controllerFactory来实现你想要的效果。当你使用controllerFactory时,在FXML文件中指定控制器的类名,但控制器工厂允许你控制如何将其映射到一个对象(因此你仍然可以构造它并传入模型等参数)。当你为FXMLLoader指定controllerFactory时,该工厂也用于创建FXML文件中任何<fx:include>的控制器。

最后,请注意,你可以将包含的FXML文件的控制器注入到“主”FXML文件中,详见FXML文档中的“嵌套控制器”一节。

所以,如果startpage.fxml看起来像这样:

<!-- imports etc -->
<BorderPane fx:controller="com.example.Startpage" ... >
  <top>
    <fx:include source="Menubar.fxml" fx:id="menubar" />
  </top>
  <!-- etc ... -->
</BorderPane>

并且 Menubar.fxml 看起来像

<!-- imports etc -->
<MenuBar fx:controller="com.example.MenubarController" ... >
  <!-- etc... -->
</MenuBar>

然后,您可以使用以下方式控制控制器类的实例化:

FXMLLoader loader = new FXMLLoader(getClass().getResource("../fxml/startpage.fxml"));

Model m = ... ;

Startpage startpageController = new Startpage(m);
MenubarController menubarController = new MenubarController(m);

Callback<Class<?>, Object> controllerFactory = type -> {
    if (type == Startpage.class) {
        return startpageController ;
    } else if (type == MenubarController.class) {
        return menubarController ;
    } else { 
        // default behavior for controllerFactory:
        try {
            return type.newInstance();
        } catch (Exception exc) {
            exc.printStackTrace();
            throw new RuntimeException(exc); // fatal, just bail...
        }
    }
};

loader.setControllerFactory(controllerFactory);

Pane mainPane = loader.load();

现在您的应用程序代码中实际上已经有了两个控制器的引用,如果需要,您也可以这样做。

public class Startpage {

    public final Model m ;

    // note the name of this field must be xController,
    // where x is the fx:id set on the <fx:include>:

    @FXML
    private final MenubarController menubarController ;

    public Startpage(Model m) {
        this.m = m ;
    }

    // ...
}

因此,主控制器现在具有对菜单栏控制器的引用。


1
好的,我需要更多的知识,现在我不理解你的回答。 编辑。 我希望它可以像“startpagecontroller扩展menucontroller”这样简单。我想我仍然会在每个控制器中保留menubar的所有方法和fxml代码.. :/ - Garog
1
除非我知道你不理解哪些部分,否则我无法真正帮助你。每个fxml文件都有一个与之关联的单个控制器实例。fxml文件可以为控制器指定一个。因此,控制器工厂只是告诉“FXMLLoader”如何从指定的类中获取控制器实例的函数。复制方法或将一个控制器作为另一个控制器的子类是没有帮助的,因为仍然有两个不同的控制器实例(因此它们不会引用相同的数据)。 - James_D
1
我最大的问题是理解你的解决方案(只是Java初学者),或将你的示例适应我的代码。 我会尝试更详细地解释为什么我需要这一切。 首先,当我开始这个项目时,每个fxml文件都有自己的控制器,这很好,我可以在不失去任何功能的情况下包含每个fxml。例如,我在应用程序“createproject”中有一个页面,其中包含一些文本字段和菜单栏。当我使用菜单项将页面(场景)切换到“settingsforcreateproject”时,我再次在新的fxml中包含了菜单栏。 - Garog
看这里https://gist.github.com/Garog/ee979f4c50c76c554f86一切都很好,比使用Swing或AWT更愉快 但是我需要从场景B中的数据到场景A,例如项目创建场景中设置场景的设置。 我在主类中创建了一个模型,然后...问题开始了,如何在控制器之间共享模型。 我四处寻找解决方案,并找到了一些听起来不错的解决方案,其中一个是设置控制器https://dev59.com/CmYq5IYBdhLWcg3wzDpI#14190310 但是这种解决方案不适用于FXML包含FXML... 现在我就卡在这里了... - Garog
大部分都是回调函数,需要先了解其功能。我需要先尝试在一个项目中运行代码,以便获得“概览”,了解发生了什么、在哪里以及为什么。如何将其与我的菜单栏 onActions 结合使用。我相信你的代码很有帮助,但我需要先使用它才能理解它。我不知道在哪里添加你的代码和如何使用它。我需要先测试一下,也许我会更清楚地看到。 - Garog
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接