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

Posted

技术标签:

【中文标题】如何使用相同的模型对象初始化 JavaFX 控制器?【英文标题】:How to initialize JavaFX controllers with the same model object? 【发布时间】:2013-12-21 18:40:00 【问题描述】:

场景

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

我的习惯

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

我目前在做什么

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

乏善可陈的替代品

(注意:我指的是这些 *** 问题:

Javafx 2.0 How-to Application.getParameters() in a Controller.java file Passing Parameters JavaFX FXML Multiple FXML with Controllers, share object

Passing parameters to a controller when loading an FXML)

依赖注入

我查看了这个网站,http://www.zenjava.com/2011/10/23/javafx-2-0-fxml-and-spring/,并且我对 google Guice 进行了一些研究,但我没有看到一种简单地为每个 JavaFX 视图/控制器提供相同模型对象的方法。似乎注入会为每个视图/控制器注入不同的模型。

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

这是一个选项,但目前我不喜欢拥有如此开放和可用的公共静态模型的想法。显然,我可以将其设为私有静态变量并拥有 getter 和 setter,但我也不太喜欢这个想法。

将参数从调用者传递给控制器​​

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

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 手动加载控制器,或者在加载控制器后设置模型。也许有一个类来检索模型是最好的选择,但我只是在问,以防万一有更好的方法。

【问题讨论】:

Passing Parameters JavaFX FXML 讨论了将数据传递到 FXML 控制器的多种机制。请检查它们。如果有任何合适的,然后用你采取的方法回答你自己的问题。如果没有一个是合适的,那么在您的问题中添加更多细节,说明您尝试了什么以及为什么它不令人满意。 +1 表示研究工作和补充细节。 【参考方案1】:

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

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

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

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

【讨论】:

我提醒您阅读过多关于 FXML 未来可能增强的论坛主题讨论。这些讨论不太可能导致对 FXML 的更改,因为讨论中的参与者不再隶属于 JavaFX。如果您希望在未来的 JavaFX 版本中考虑某些特定功能,create a feature request。【参考方案2】:

更新

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

Gluon Ignite 允许开发人员在他们的 JavaFX 应用程序中使用流行的依赖注入框架,包括在他们的 FXML 控制器中。 Gluon Ignite 在几个流行的依赖注入框架(目前是 Guice、Spring 和 Dagger,但我们计划在需求变得明显时添加更多)创建一个通用抽象。完全支持 JSR-330 Gluon Ignite 使得在 JavaFX 应用程序中使用依赖注入变得轻而易举。

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

建议的方法

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

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

替代依赖注入系统

Spring 庞大而复杂,除非您的应用程序需要很多其他功能,否则由于其复杂性,不应考虑使用它。

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

所以,试试 afterburner.fx 看看它是否符合您的需求。

afterburner.fx 示例代码

这是使用 afterburner.fx 将模型实例 (NotesStore) 注入控制器的示例。样本直接复制自afterburner.fx documentation。

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 时确实遇到了一些问题,所以我forked it's code 并修复了一些阻止我开箱即用的问题。

cmets补充问题的答案

那么从 NoteStore 示例中,您是说我所要做的就是添加 afterburner 框架依赖项并将 @Inject 放在我的模型变量上?

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

我尝试了 google guice 并让它工作。 . .您会在构造函数中看到手动注入游戏设置对象。

我认为您不应该像那样手动使用 Guice 注射器。我认为您可以在 FXMLLoader 实例上set a controller factory 来启动注入。这就是 afterburner.fx 中的 FXMLView 的作用。 Guice 中使用的注入机制的确切细节与 afterburner.fx 机制不同,但我认为设置控制器工厂的广义概念仍然相似。

在Associating FXML and Controller in Guice's Module configuration的回答中有一个使用 FXML 和 Guice 设置控制器工厂的演示。

很遗憾,没有一种更直接的方法不会给您带来很多困难。

作为一个无关紧要的个人旁注,我对依赖注入框架的话题总体上有点矛盾。当然,它们可以提供帮助,但是对于简单的事情,我通常对带有 getInstance 方法的单例而不是更复杂的框架感到满意。不过我确实看到它们在大型项目中是如何有用的,当然它们在某些 Java 框架中非常流行。

【讨论】:

那么从 NoteStore 示例中,您是说我所要做的就是添加 afterburner 框架依赖项并将 @Inject 放在我的模型变量上吗?因为我试过了,但没有用:--- public class MainController extends Tab `@Inject private GameSettings gameSettings; ... 我尝试格式化此文件,但效果不佳。 我尝试了 google guice 并让它工作,但我遇到了一些问题。例如,如果您查看此文件 github.com/quicksilversly/Dominator/blob/master/src/main/java/…,您将在构造函数中看到手动注入游戏设置对象。我认为这种方式失去了 DI 的好处。我认为它的父级 MainController 通过 @FXML 注入 SingleGameSetupController 没有帮助。 根据其他问题更新了答案。【参考方案3】:

DataFX 框架有一个名为 DataFX-Flow 的新 API。使用此 API,您可以简单地将对象注入视图控制器。此处支持 Afterburner.fx 范围的其他内容。你可以在这里找到一个例子:http://www.guigarage.com/2013/12/datafx-controller-framework-preview/

【讨论】:

以上是关于如何使用相同的模型对象初始化 JavaFX 控制器?的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX:如何在初始化期间从控制器获取阶段?

初始化后,在javafx中的控制器之间传递变量[duplicate]

JavaFX项目结构

我可以使用 FileChooser (javafx) 将序列化对象保存在文件中吗?

在 JavaFX FXML 控制器中实现构造函数

JavaFX如何改变阶段