问题描述
场景
我正在创建一个GUI,其中多个视图引用同一模型对象。
$ b
我习惯的
在Swing中,如果我想要所有视图引用相同的模型,我会将模型传递给构造函数。
$ b
我目前正在做什么
在JavaFX中,我传递模型在每个视图/控制器加载后,在views / controllers(菜单栏,分割窗格,选项卡,...)中设置一个setter方法。我觉得这很粘和麻烦。此外,我发现它不会工作,因为在某些情况下,我需要模型已经存在于控制器中,在一些控制器小部件初始化之前。
Lackluster替代品
(注意:我提到这些stackoverflow问题:
-
,我已经看了一点谷歌Guice,但我没有看到一种简单地给每个JavaFX视图/控制器相同的模型对象的方法。看起来像注入会为每个视图/控制器注入不同的模型。
将模型对象保存为公共静态变量
- 这是一个选项,但目前我不喜欢有一个公开的静态模型这么开放和可用。显然,我可以使它成为一个私人静态变量,并有getters和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控制器tab自动注入变量:
public class SingleGameSetupController extends AnchorPane {
@FXML private CardOverviewTab cardOverviewTab ;
//类别的剩余
}
-
包含卡概览选项卡的fxml部分如下所示:
< CardOverviewTab fx:id =cardOverviewTab />
-
这种方式我不需要担心手动加载控制器,设置模型的问题。
在FXMLLoader上设置控制器
- 此选项与我习惯的类似,将模型作为参数传递到构造函数中,但它仍然有使用FXMLLoader手动加载控制器的问题。
活动巴士
- 我没有读过太多的东西,但从我读过的事件总线似乎是无效的和过时的。
- 这类似于对控制器可以检索的模型对象具有公共静态引用,但是我正在寻找更好的解决方案。此外,我不想要单身模型。
找找
有没有办法以较不繁琐的方式传递模型对象?我正在寻找一种方法,就像将模型传递给构造函数一样简单,但我不想处理通过FXMLLoader手动加载控制器,或在控制器加载后设置模型。也许有一个类来检索模型是最好的选择,但我要求,以防万一有更好的方法。
更新
除了afterburner.fx之外,还可以检出:
@Inject,类似于afterburner.fx。
建议的方法
似乎在寻求依赖注入框架,我认为最好的选择是使用。
afterburner.fx提供了一种使用标准Java将模型对象注入到JavaFX控制器中的方法注释。
替代依赖注入系统
Spring规模庞大且复杂,除非需要大量的其他功能为您的应用程序,不应该考虑由于它的复杂性。
Guice比Spring更简单,如果你需要一个合理的选择依赖注入框架与许多功能如提供程序类。但是从它的声音,你不需要Guice提供的所有功能,因为你只是想要一个方法来传递对象的应用程序中的单例实例,而不显式查找它们。
因此,请尝试afterburner.fx,看看是否符合您的需求。
afterburner.fx示例代码 p>
这里是使用afterburner.fx将模型实例( NotesStore
)注入到控制器中的示例。示例直接从复制。
import com.airhacks.afterburner.views.FXMLView;
public class NoteListView扩展FXMLView {
//通常什么都不做,FXML和CSS自动
//加载和实例化
}
public class AirpadPresenter implements可初始化{
@Inject //由加力器注入,需要零配置
NotesStore store;
@FXML //由FXML注入
AnchorPane noteList;
@Override
public void initialize(URL url,ResourceBundle rb){
//从FXML构造的视图
NoteListView noteListView = new NoteListView();
//从FXML获取和集成视图
父视图= noteListView.getView();
this.noteList.getChildren()。add(view);
}
}
是一个基本的示例应用程序,演示如何使用afterburner.fx。我确实有一些问题让followme.fx运行直接开箱,由于Maven依赖不兼容,所以我在FXMLLoader实例上启动注入。这是在afterburner.fx中。在Guice中使用的注入机制的确切细节将不同于afterburner.fx机制,但我认为设置控制器工厂的广泛概念保持类似。
有一个演示的集控制器工厂使用FXML和Guice的答案:在Guice的模块配置中关联FXML和Controller。
这是一个耻辱,没有一个更直接的方法,这不会导致你这么多困难。
作为一个无关紧要的个人注释,我对一般的依赖注入框架的主题有些矛盾。当然,他们可以帮助,但很多时候对于简单的事情,我经常用一个单例与一个getInstance方法,而不是更复杂的框架。仍然我看看如何在大型项目,他们可以是有用的,当然他们在某些Java框架中非常受欢迎。
Scenario
I am creating a GUI where multiple views reference the same model object.
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.
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.
Lackluster Alternatives
(Note: I am referencing these stackoverflow questions:
- 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.
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.
Update
In addition to afterburner.fx, also checkout Gluon Ignite:
Injection of model objects into controllers is also via @Inject, similar to 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 provides a way injecting model objects into your JavaFX controllers using the standard Java @Inject annotation.
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.
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.
So, try out afterburner.fx and see if it fits your needs.
afterburner.fx Sample Code
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.
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 is 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 code and fixed some of the issues which prevented me from using it out of the box.
Answers to addition questions from comments
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.
I don't think you should have to use the Guice injector manually like that. I think you can set a controller factory on an FXMLLoader instance to initiate the injection. This is how the FXMLView in 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.
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.
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.
这篇关于如何使用相同的模型对象初始化JavaFX控制器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!