如何使用相同的模型对象初始化 JavaFX 控制器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20389567/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to initialize JavaFX controllers with the same model object?
提问by j will
Scenario
设想
I am creating a GUI where multiple views reference the same model object.
我正在创建一个 GUI,其中多个视图引用相同的模型对象。
What I am Accustom to
我习惯的
In Swing, if i want all the views to reference the same model i would pass the model into the constructor.
在 Swing 中,如果我希望所有视图都引用同一个模型,我会将模型传递给构造函数。
What I am Currently Doing
我目前在做什么
In JavaFX, I am passing the model around by having a setter method in the views/controllers (menubars, split panes, tabs, ...), after each view/controller has been loaded. I find this very tacky and cumbersome. Additionally, I find it won't work because in certain situations i need the model to already exist in a controller before some of the controller widgets are initialized.
在 JavaFX 中,在加载每个视图/控制器后,我通过在视图/控制器(菜单栏、拆分窗格、选项卡等)中使用 setter 方法来传递模型。我觉得这非常俗气和麻烦。此外,我发现它不起作用,因为在某些情况下,我需要模型在初始化某些控制器小部件之前已经存在于控制器中。
Lackluster Alternatives
乏善可陈的替代品
(Note: I am referencing these stackoverflow questions:
(注意:我参考了这些 stackoverflow 问题:
- Javafx 2.0 How-to Application.getParameters() in a Controller.java file
- Passing Parameters JavaFX FXML
- Multiple FXML with Controllers, share object
Dependency Injection
- I've looked at this website, http://www.zenjava.com/2011/10/23/javafx-2-0-fxml-and-spring/, and i have looked a little into google Guice, but I don't see a way of simplistically giving each JavaFX view/controller the same model object. It seemed like the injection would inject a different model for each view/controller.
Saving the model object as a public static variable
- This is an option but at the moment I don't like the idea of having a public static model so open and available. Obviously, I can make it a private static variable and have getters and setters, but i don't like this idea much either.
Passing Parameters from Caller to Controller
I want each controller to load itself in its constructor, and I want each custom controller to be automatically injected into its parent controller. For example, the card overview tab loads itself like this:
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(); } }
And the SingleGameSetup controller has the card overview tab automatically injected into a variable:
public class SingleGameSetupController extends AnchorPane { @FXML private CardOverviewTab cardOverviewTab; // Rest of the class }
And the part of the fxml containing the card overview tab looks like this:
<CardOverviewTab fx:id="cardOverviewTab" />
This way I do not need to worry about manually loading a controller, but I still have the problem of setting the model.
Setting a Controller on the FXMLLoader
- This option is similar to what I am accustom to, passing the model as a parameter into the constructor, but it still has the problem of loading controllers manually with the FXMLLoader.
Event Bus
- I haven't read too much into this, but from what I have read the event bus seems to be inactive and outdated.
Singleton
- This is similar to having a public static reference to the model object that controllers can retrieve, but again I am looking for a better solution. Plus, I do not want a singleton model.
- Controller.java 文件中的 Javafx 2.0 How-to Application.getParameters()
- 传递参数 JavaFX FXML
- 带有控制器的多个 FXML,共享对象
依赖注入
- 我看过这个网站,http://www.zenjava.com/2011/10/23/javafx-2-0-fxml-and-spring/,我对谷歌 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手动加载控制器的问题。
事件总线
- 我没有读太多,但从我读到的事件总线似乎是不活动的和过时的。
单身人士
- 这类似于拥有对控制器可以检索的模型对象的公共静态引用,但我再次寻找更好的解决方案。另外,我不想要单例模型。
What I am Looking for
我在寻找什么
Is there a way to pass the model object around in a less cumbersome way? I am looking for a way that is as simple as passing the model to a constructor, but I do not want to deal with manually loading controllers via the FXMLLoader, or setting the model after the controllers are loaded. Maybe having a class to retrieve the model is the best option, but I am asking just in case there is a better way.
有没有办法以不那么麻烦的方式传递模型对象?我正在寻找一种与将模型传递给构造函数一样简单的方法,但我不想通过 FXMLLoader 手动加载控制器,或者在加载控制器后设置模型。也许有一个类来检索模型是最好的选择,但我只是问以防万一有更好的方法。
采纳答案by jewelsea
Update
更新
In addition to afterburner.fx, also checkout Gluon Ignite:
除了 afterburner.fx,还要检查Gluon Ignite:
Gluon Ignite allows developers to use popular dependency injection frameworks in their JavaFX applications, including inside their FXML controllers. Gluon Ignite creates a common abstraction over several popular dependency injection frameworks (currently Guice, Spring, and Dagger, but we plan at add more as the demand becomes obvious). With full support of JSR-330 Gluon Ignite makes using dependency injection in JavaFX applications trivial.
Gluon Ignite 允许开发人员在他们的 JavaFX 应用程序中使用流行的依赖注入框架,包括在他们的 FXML 控制器中。Gluon Ignite 在几个流行的依赖注入框架(目前是 Guice、Spring 和 Dagger,但我们计划在需求变得明显时添加更多)创建一个通用抽象。完全支持 JSR-330 Gluon Ignite 使得在 JavaFX 应用程序中使用依赖注入变得微不足道。
Injection of model objects into controllers is also via @Inject, similar to afterburner.fx.
将模型对象注入控制器也是通过@Inject,类似于 afterburner.fx。
Suggested Approach
建议的方法
As you appear to be seeking a dependency injection framework, I think your best option is to use the afterburner.fx framework.
由于您似乎正在寻找依赖注入框架,我认为您最好的选择是使用afterburner.fx 框架。
afterburner.fx provides a way injecting model objects into your JavaFX controllers using the standard Java @Injectannotation.
afterburner.fx 提供了一种使用标准 Java @Inject注释将模型对象注入 JavaFX 控制器的方法。
Alternative Dependency Injection Systems
替代依赖注入系统
Spring is large and complicated and, unless you need a lot of its other functionality for your application, should not be considered due to its complexity.
Spring 庞大而复杂,除非您的应用程序需要它的许多其他功能,否则由于其复杂性,不应考虑它。
Guice is a lot simpler than Spring and a reasonable pick if you need a dependency injection framework with numerous features such as provider classes. But from the sound of it, you don't need all the features that Guice provides as you just want to a way to pass around singleton instances of objects in your application without explicitly looking them up.
Guice 比 Spring 简单得多,如果您需要一个具有众多功能(例如提供程序类)的依赖项注入框架,那么它是一个合理的选择。但是从它的声音来看,您不需要 Guice 提供的所有功能,因为您只是想要一种在应用程序中传递对象的单例实例的方法,而无需显式查找它们。
So, try out afterburner.fx and see if it fits your needs.
因此,请尝试 afterburner.fx 并查看它是否符合您的需求。
afterburner.fx Sample Code
afterburner.fx 示例代码
Here is sample of injecting a model instance (the NotesStore
) into a controller using afterburner.fx. The sample is directly copied from the afterburner.fx documentation.
这是NotesStore
使用 afterburner.fx将模型实例 (the ) 注入控制器的示例。该示例直接从afterburner.fx 文档中复制而来。
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.fxis a basic sample application demonstrating how to use afterburner.fx. I did have a few issues getting followme.fx running straight out of the box due to Maven dependency incompatibilities, so I forked it's codeand fixed some of the issues which prevented me from using it out of the box.
followme.fx是一个基本的示例应用程序,演示了如何使用 afterburner.fx。由于 Maven 依赖项不兼容,我确实在让 followme.fx 开箱即用时遇到了一些问题,所以我分叉了它的代码并修复了一些阻止我开箱即用的问题。
Answers to addition questions from comments
对评论中附加问题的回答
So from the NoteStore example, are you saying all I have to do is add the afterburner framework dependency and put @Inject on my model variable?
因此,从 NoteStore 示例中,您是说我所要做的就是添加 afterburner 框架依赖项并将 @Inject 放在我的模型变量上?
No, you also need to create an associated class that extends FXMLView and instantiate that with a new call (similar to how NotesListView is created in the sample code above). If you are interested in continuing to investigate the afterburner.fx framework, then use the followme.fx project as basis because it provides complete source code for a very simple executable sample using the framework.
不,您还需要创建一个关联类来扩展 FXMLView 并使用新调用对其进行实例化(类似于在上面的示例代码中创建 NotesListView 的方式)。如果您有兴趣继续研究 afterburner.fx 框架,请使用 followme.fx 项目作为基础,因为它为使用该框架的非常简单的可执行示例提供了完整的源代码。
I tried google guice and got it to work . . . you'll see in the constructor a game settings object is injected manually.
我试过谷歌 guice 并让它工作。. . 您将在构造函数中看到手动注入游戏设置对象。
I don't think you should have to use the Guice injector manually like that. I think you can set a controller factoryon an FXMLLoader instance to initiate the injection. This is how the FXMLViewin afterburner.fx does it. The exact detail of the injection mechanism used in Guice is going to differ from the afterburner.fx mechanism, but I think the broad concept of setting the controller factory remains similar.
我认为您不应该像那样手动使用 Guice 注入器。我认为您可以在 FXMLLoader 实例上设置控制器工厂来启动注入。afterburner.fx 中的FXMLView就是这样做的。Guice 中使用的注入机制的确切细节将与 afterburner.fx 机制不同,但我认为设置控制器工厂的广泛概念仍然相似。
There is a demo of the set controller factory using FXML and Guice in the answer to: Associating FXML and Controller in Guice's Module configuration.
在回答:Associating FXML and Controller in Guice's Module configuration 中有一个使用 FXML 和 Guice 的设置控制器工厂的演示。
It's a shame there is not a more straightforward way of doing this which does not cause you so many difficulties.
很遗憾,没有一种更直接的方法可以做到这一点,不会给您带来这么多困难。
As an inconsequential personal side note, I'm kind of ambivalent on the topic of dependency injection frameworks in general. Sure, they can help, but many times for simple things I'm often OK with a singleton with a getInstance method rather than the more sophisticated framework. Still I do see how in larger projects they can be useful and certainly they are very popular in certain Java frameworks.
作为一个无关紧要的个人旁注,我对依赖注入框架的一般话题有点矛盾。当然,它们可以提供帮助,但很多时候对于简单的事情,我通常可以使用带有 getInstance 方法的单例,而不是更复杂的框架。我仍然看到它们在较大的项目中是如何有用的,当然它们在某些 Java 框架中非常流行。
回答by j will
I have been researching this problem and I have found that dependency injection along with a singleton (for the model) is my optimal design. I learned that having a setter method for the model in each of my controllers is a form of dependency injection as is passing the model through the constructor. Spring and Guice are frameworks to help that.
我一直在研究这个问题,我发现依赖注入和单例(对于模型)是我的最佳设计。我了解到在我的每个控制器中为模型设置一个 setter 方法是一种依赖注入的形式,就像通过构造函数传递模型一样。Spring 和 Guice 是帮助实现这一目标的框架。
I tried using Spring as noted here: http://www.zenjava.com/2011/10/23/better-controller-injection/but I ran into the problem when the configuration class tried to load a controller, it failed to load the member controllers. This may not have been a Spring issue, but I figured I didn't care enough to spend the time to get it working. Plus, I would have had to go through all of my controller files and edited the way they were created.
我尝试使用 Spring,如下所述:http: //www.zenjava.com/2011/10/23/better-controller-injection/但是当配置类尝试加载控制器时我遇到了问题,但加载失败成员控制者。这可能不是 Spring 问题,但我认为我不太关心花时间让它工作。另外,我将不得不浏览我所有的控制器文件并编辑它们的创建方式。
Having searched around and doing a lot of reading, I found that this article best expresses what I would like to be able to do: https://forums.oracle.com/thread/2301217?tstart=0. Since the article is referring to suggested improvements, these improvements do not yet exist in JavaFX. But when they do, I will be implementing them. Just for an example, being able to inject a model via the fxml:
四处搜索并阅读大量内容后,我发现这篇文章最能表达我希望能够做的事情:https: //forums.oracle.com/thread/2301217?tstart=0。由于本文提到了建议的改进,因此 JavaFX 中尚不存在这些改进。但是当他们这样做时,我将实施它们。仅举个例子,能够通过 fxml 注入模型:
<usevar name="model" type="com.mycom.myapp.ModelObject"/>
回答by Hendrik Ebbers
The DataFX Framework has a new API called DataFX-Flow. With this API you can simply inject objects in a view controller. Other that Afterburner.fx Scopes are supported here. You can find an example here: http://www.guigarage.com/2013/12/datafx-controller-framework-preview/
DataFX 框架有一个名为 DataFX-Flow 的新 API。使用此 API,您可以简单地在视图控制器中注入对象。此处支持 Afterburner.fx 范围的其他内容。你可以在这里找到一个例子:http: //www.guigarage.com/2013/12/datafx-controller-framework-preview/