【问题标题】:Splitting up JavaFX Controller Class拆分 JavaFX 控制器类
【发布时间】:2018-09-17 05:44:10
【问题描述】:

我没有 MCV 代码示例,因为问题更具全局性,我认为具体的代码实际上并没有太大的区别。

我有一个 GUI 应用程序,其中包含多个元素(日期选择器、下拉列表、输入字段、tabletreeview)以及根据 JavaFX 的一个 ControllerClass,它随着时间的推移变得相当大。

因此,我正在寻找一种方法来拆分它。在我的研究中,我发现了两种主要方法:

从 UI 部分获取更多程序逻辑,这当然总是有意义的,我对某些部分这样做了,但对于许多人来说,这似乎很不方便,例如,如果我有一个使用六个不同 GUI 元素的函数从控制器类中提取该方法似乎很奇怪,因为我需要在函数调用中为它提供所有这些 GUI 元素引用。我错过了一些更好的默认方式吗?就像只使​​用 setter/getter 填充控制器类并将每个方法 this 作为参数,以便它可以转到 this.dropbox1

我发现的第二种方法是将 GUI 本身分成几个场景。就像this 问题一样,但我不知道如何将一个 GUI 拆分为多个场景并仍然将其放在一个窗口中。我确实意识到您可以在主 FXML 文件中加载其他 FXML 文件,但我不确定如何在一个窗口中将它们组合到 GUI 中。如果有人对此有一些示例代码,我将不胜感激。

【问题讨论】:

  • 认为“模型”而不是“功能”或“用户界面”:用户操作的数据具有必须在一个或多个数据类中建模的内在逻辑/关系,这些可以移动视图除了监听数据属性的变化或通过数据对象上的专用 api 更改它们之外什么都不做
  • MVC 概念对于高级程序员来说可能具有挑战性。我仍在努力掌握它。 @James_D 的这个例子是一个很好的例子,它提供了一个简单的例子来帮助开发人员理解一种形式的 MVC。 stackoverflow.com/questions/32342864/applying-mvc-with-javafx
  • 假设您有一个包含 10 个选项卡的 TabbedPane。每个选项卡都包含不同的 UI。在这种情况下,您不必通过一个控制器控制多个选项卡下的每个 UI 元素。您将创建 10 个单独的 fxml 文件,每个文件都有控制器类,然后在用户更改选项卡时在选项卡下加载每个 fxml 文件。这样你就不会得到一个巨大的控制器

标签: java javafx


【解决方案1】:

欢迎来到 Stackoverflow Labib,关于分拆:

您可以有一个根 FXML,例如只包含一个 GridPane(或其他,但我将继续在 GridPane 前提下),现在您在该根 FXML 文件中设置网格(无内容),如果您已经完成了 GUI,您应该清楚地知道网格单元需要多大。在您的主类中,您有:

扩展应用程序并启动 GUI 的主类

public class GUI extends Application {

    private Stage stage;
    private GridPane rootGrid;

    @Override
    public void start(Stage stage) {

        this.stage = stage;

        // Load root layout from fxml file.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(WindowRoot.class.getResource("RootGrid.fxml"));
        rootGrid = (GridPane) loader.load();

        // Show the scene containing the root layout.
        Scene scene = new Scene(rootGrid);
        stage.setTitle("Test");
        stage.show();
        //fill the Grid
        setupGrid();
    }

现在您将rootGrid 作为类属性,它只是一个在RootGrid.fxml 中设置的带有空行/列的网格,可以简单如下:

FXML 示例,两行两列,无内容

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

<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>


<GridPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141">
   <rowConstraints>
      <RowConstraints minHeight="10.0" prefHeight="30.0" />
      <RowConstraints minHeight="10.0" prefHeight="30.0" />
   </rowConstraints>
   <columnConstraints>
      <ColumnConstraints minWidth="10.0" prefWidth="100.0" />
      <ColumnConstraints minWidth="10.0" prefWidth="100.0" />
   </columnConstraints>
</GridPane>

现在您为要填充的每个单元格创建 FXML 文档,并且在该 FXML 中您有一个包含该单元格内容的 AnchorPane。然后将其作为start 方法的一部分添加到网格中。假设您在这个简单的示例中想要一个菜单​​栏。为菜单创建 FXML:

简单菜单 fxml 示例:

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

<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141">
   <children>
      <MenuBar>
        <menus>
          <Menu mnemonicParsing="false" text="File">
            <items>
              <MenuItem mnemonicParsing="false" text="Close" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </children>
</AnchorPane>

并将其作为start 方法的一部分添加到网格中(但可能将其提取到您在开始时调用的方法中,例如setupGrid()

设置 Grid,在 start(Stage stage) 中调用

private void setupGrid() {
    // Add Menubar
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(WindowRoot.class.getResource("menubar.fxml"));
    AnchorPane anchor = (AnchorPane) loader.load();
    //put it in first column, first row
    GridPane.setConstraints(anchor, 0, 0);
    //optional, let it span both/all columns
    GridPane.setRowSpan(anchor, 2);
    rootGrid.getChildren().add(anchor);

您需要 try/except IO 块,为了更好的可读性,将它们排除在外。对于其他“根窗格”,您将需要其他设置方法,例如使用borderPane,您将拥有rootGrid.setTop(anchor) 之类的东西。
希望能帮助到你。玩得开心。

【讨论】:

  • 感谢您提供详细的答案,很好地解释了 FXML 的分离,并设法做到了这一点。只是他们之间的沟通有问题。
【解决方案2】:

如果你使用 FXML,简单的事情就是有一个 FXML 的控制器(一对一)而且我个人还添加了一个 css,这是一种模式。

然后您必须将屏幕分成小部分:2种方法:

  • 使用 fx:include :您包含一个 FXML,它本身声明了一个控制器
  • 使用组件:您包含组件的标签,该标签本身可以加载 FXML 和控制器

然后你必须在你的控制器之间进行通信,最好是有一个模型层,并通过单例共享它,例如使用 afterburnerfx(一个非常小的注入器)

使用 fx:include 时,您还可以使用“嵌套控制器”从父级获取子控制器的引用。

最后,您的对象可能是带有 observable 的 javaFX Bean,并且您只将可观察组件从控制器暴露给另一个控制器,而不是可视组件:这样您的组件之间就有了松散耦合。

【讨论】:

  • Singleton 建议听起来像是我的一个想法,将 Controller 引用放在静态类中(尽管我实际上没有想到 Singleton 模板。我查看了 afterburnerfx 框架,但不是不知道这会有什么帮助。我目前的结构类似于@Bernhard 提出的结构。
  • AfterburnerFx 非常简单,在指向单例的控制器属性上使用 @Inject 注释,并在控制器的构造函数和服务中调用 Injector 的方法,例如服务构造函数 Injector.registerExistingAndInject(this)Injector.injectMembers(getClass(),this)对于控制器构造函数。优点是服务实例化和递归实例化的自动化(如果服务使用服务)
猜你喜欢
  • 1970-01-01
  • 2016-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-11
相关资源
最近更新 更多