如何使用相同的模型对象初始化 JavaFX 控制器?

场景

我正在创建一个 GUI,其中多个视图引用同一个模型对象。

我习惯了什么

在 Swing 中,如果我希望所有视图都引用同一模型,我会将模型传递到构造函数中。

我目前在做什么

在JavaFX中,在加载每个视图/控制器后,我通过在视图/控制器(菜单栏,拆分窗格,选项卡等)中使用setter方法来传递模型。我发现这非常俗气和繁琐。此外,我发现它不起作用,因为在某些情况下,我需要模型已经存在于控制器中,然后才能初始化某些控制器小部件。

乏善可陈的替代品

(注意:我引用了以下堆栈溢出问题:

  • 控制器文件中的 Javafx 2.0 How-to Application.getParameters() .java
  • 传递参数 JavaFX FXML
  • 多个 FXML 与控制器,共享对象
  • 加载 FXML 时将参数传递到控制器)

  • 依赖注入

  • 将模型对象另存为公共静态变量

    • 这是一个选项,但目前我不喜欢让一个公共静态模型如此开放和可用的想法。显然,我可以把它变成一个私有的静态变量,并有getter和setters,但我也不喜欢这个想法。
  • 将参数从调用方传递到控制器

    • 我希望每个控制器在其构造函数中加载自身,并且我希望每个自定义控制器自动注入其父控制器。例如,卡片概览选项卡按如下方式加载自身:

      public CardOverviewTab() {
          FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("card_overview_tab.fxml"));
          fxmlLoader.setRoot(content);
          fxmlLoader.setController(this);
      
          try {
              fxmlLoader.load();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      
    • SingleGameSetup控制器将卡片概述选项卡自动注入到变量中:

      public class SingleGameSetupController extends AnchorPane {
      
          @FXML private CardOverviewTab cardOverviewTab;
      
          // Rest of the class
      }
      
    • fxml中包含卡概述选项卡的部分如下所示:

      <CardOverviewTab fx:id="cardOverviewTab" />
      
    • 这样我就不需要担心手动加载控制器,但我仍然有设置模型的问题。

  • 在 FXMLLoader 上设置控制器

    • 此选项类似于我习惯的选项,将模型作为参数传递到构造函数中,但它仍然存在使用FXMLLoader手动加载控制器的问题。
  • 事件总线

    • 我没有读太多,但从我所读到的内容来看,事件总线似乎不活跃且过时。
  • 单身 人士

    • 这类似于对控制器可以检索的模型对象进行公共静态引用,但我再次正在寻找更好的解决方案。另外,我不想要单例模型。

我在寻找什么

有没有办法以一种不那么麻烦的方式传递模型对象?我正在寻找一种像将模型传递给构造函数一样简单的方法,但我不想处理通过FXMLLoader手动加载控制器,或者在加载控制器后设置模型。也许有一个类来检索模型是最好的选择,但我问以防万一有更好的方法。


答案 1

更新

除了 afterburner.fx 之外,还可以查看 Gluon Ignite

Gluon Ignite允许开发人员在其JavaFX应用程序中使用流行的依赖注入框架,包括在其FXML控制器中。Gluon Ignite在几个流行的依赖注入框架(目前是Guice,Spring和Dagger)上创建了一个共同的抽象,但是随着需求变得明显,我们计划添加更多)。由于完全支持JSR-330 Gluon Ignite,使得在JavaFX应用程序中使用依赖注入变得微不足道。

将模型对象注入控制器也是通过@Inject,类似于 afterburner.fx。

建议的方法

由于您似乎正在寻找依赖注入框架,我认为您最好的选择是使用afterburner.fx框架

afterburner.fx 提供了一种使用标准 Java @Inject注释将模型对象注入 JavaFX 控制器的方法。

替代依赖注入系统

Spring体积大且复杂,除非您需要很多其他功能来应用,否则由于其复杂性,不应考虑Spring。

Guice比Spring简单得多,如果您需要一个具有众多功能(例如提供者类)的依赖注入框架,则可以合理选择。但从它的声音来看,您不需要Guice提供的所有功能,因为您只想找到一种方法来传递应用程序中对象的单例实例,而无需显式查找它们。

因此,请尝试afterburner.fx,看看它是否符合您的需求。

afterburner.fx 示例代码

下面是使用 afterburner.fx 将模型实例 (the ) 注入控制器的示例。该示例直接从 afterburner.fx 文档中复制。NotesStore

import com.airhacks.afterburner.views.FXMLView;

public class NoteListView extends FXMLView {
    //usually nothing to do, FXML and CSS are automatically
    //loaded and instantiated
}

public class AirpadPresenter implements Initializable {    
    @Inject // injected by afterburner, zero configuration required
    NotesStore store;

    @FXML // injected by FXML
    AnchorPane noteList;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        //view constructed from FXML
        NoteListView noteListView = new NoteListView();

        //fetching and integrating the view from FXML
        Parent view = noteListView.getView();
        this.noteList.getChildren().add(view);
    }
}

followme.fx 是一个基本的示例应用程序,演示了如何使用 afterburner.fx。由于Maven依赖性不兼容,我确实遇到了一些问题,使 followme.fx 开箱即用,所以我分叉了它的代码并修复了一些阻止我开箱即用的问题。

评论中附加问题的答案

因此,从NoteStore的例子中,你是说我所要做的就是添加加力燃烧器框架依赖项,并在我的模型变量上放置@Inject?

不,您还需要创建一个扩展 FXMLView 的关联类,并使用新调用对其进行实例化(类似于在上面的示例代码中创建 NotesListView 的方式)。如果您有兴趣继续研究 afterburner.fx 框架,请使用 followme.fx 项目作为基础,因为它为使用该框架的非常简单的可执行示例提供了完整的源代码。

我尝试了谷歌指导,并让它工作...您将在构造函数中看到手动注入的游戏设置对象。

我不认为你应该像那样手动使用Guice注入器。我认为您可以在FXMLLoader实例上设置控制器工厂以启动注入。这就是 afterburner.fx 中的 FXMLView 是如何做到的。Guice中使用的注入机制的确切细节将与afterburner.fx机制不同,但我认为设置控制器工厂的广泛概念仍然相似。

在答案中,有一个使用FXML和Guice的集合控制器工厂的演示:在Guice的模块配置中关联FXML和控制器

很遗憾,没有比这更直接的方法可以做到这一点,这不会给你带来很多困难。

作为一个无关紧要的个人旁注,我对依赖注入框架的话题有点矛盾。当然,它们可以提供帮助,但是很多时候对于简单的事情,我经常可以使用具有getInstance方法的单例,而不是更复杂的框架。我仍然看到它们在大型项目中是如何有用的,当然它们在某些Java框架中非常受欢迎。


答案 2

我一直在研究这个问题,我发现依赖注入和单例(对于模型)是我的最佳设计。我了解到,在我的每个控制器中为模型使用 setter 方法是一种依赖注入形式,就像通过构造函数传递模型一样。Spring和Guice是帮助实现这一目标的框架。

我尝试使用Spring,如下所述:http://www.zenjava.com/2011/10/23/better-controller-injection/ 但是当配置类尝试加载控制器时,我遇到了问题,它无法加载成员控制器。这可能不是春季的问题,但我认为我不够在乎花时间让它工作。另外,我必须遍历我所有的控制器文件,并按照它们的创建方式进行编辑。

在搜索并进行了大量阅读后,我发现这篇文章最能表达我希望能够做的事情:https://forums.oracle.com/thread/2301217?tstart=0。由于本文指的是建议的改进,因此这些改进在JavaFX中尚不存在。但是当他们这样做时,我将实施它们。举个例子,能够通过fxml注入模型:

<usevar name="model" type="com.mycom.myapp.ModelObject"/>

推荐