使用Guice将数据注入JavaFX ViewController

时间:2016-03-22 21:02:22

标签: java javafx guice

今天我将Guice添加到我的Java FX应用程序中。主要目标是用注入替换我的单例并打破依赖关系。 到目前为止一切正常,这是我必须开始一个新场景的代码:

public class App extends Application{

public static void main(String[] args){
    launch(args);
}

@Override
public void start(Stage primaryStage) throws Exception {
    final String LANGUAGE_BUNDLE = "myBundlePath";
    final String FXML = "myFXMLPath";

    try {
        ResourceBundle resourceBundle = ResourceBundle.getBundle(LANGUAGE_BUNDLE, Locale.GERMAN, this.getClass().getClassLoader());
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(FXML), resourceBundle, new JavaFXBuilderFactory(), getGuiceControllerFactory());
        primaryStage.setScene(new Scene(fxmlLoader.load()));
        primaryStage.show();
    }catch (IOException ex) {
        ex.printStackTrace();
    }
}

private Callback<Class<?>, Object> getGuiceControllerFactory(){
    Injector injector = Guice.createInjector(new GuiceModule());
    return new Callback<Class<?>, Object>() {
        @Override
        public Object call(Class<?> clazz) {
            return injector.getInstance(clazz);
        }
    };
}
}

我的Guice模块看起来像这样:

public class GuiceModule extends AbstractModule {
@Override
protected void configure() {
    bind(ITester.class).to(Tester.class);
    bind(ISecondTest.class).to(SecondTest.class);
}
}

请注意,我替换了ressource包和fxml文件的路径,因为它们会显示我的身份。但加载和一切都有效,所以这不应该是一个问题;)

现在问题是,我想在不同的视图中点击按钮来实例化一个新视图。第二个视图应在视图1中显示更详细的数据版本。 我需要传递给第二个视图的所有东西都是Integer(或int),但我完全不知道如何做到这一点。

我有标准的FX设置:

View.fxml (with a reference to the ViewController)
ViewController.java
Model.java
ViewModel.java

ViewController然后绑定到ViewModel提供的属性。 我需要int来选择正确的模型。

我能找到的一切都是关于注释@Named但是据我所知,这对于注入动态数据是不可用的。

你能不能给我一个提示,告诉我这个我想要做的是什么? 或者长话短说:如何在第二个ViewController中注入一个由不同视图选择的变量,并将其余部分留在标准的FX方式中?

任何帮助表示感谢并提前致谢!

此致,基督徒

2 个答案:

答案 0 :(得分:3)

在尝试了一下之后,似乎我自己找到了解决方案! 然而,它“感觉”我正在做的很难看,所以我想要一些确认;)

首先理论:Guice支持“AssistedInject”。这是,当一个类不能由默认构造函数构造时。为了能够使用AssistedInject,您必须下载扩展名(我从maven repository下载了jar)。

AssistedInject为您做的是它允许您指定为您构建变量的工厂。所以这就是我所做的:

首先,为我稍后要使用的类创建一个接口,在我的例子中:

public interface IViewController {
}

其次,为工厂创建一个界面。重要提示:您不必实施工厂

public interface IControllerFactory {

    ViewController create(@Assisted int myInt);

}

第三,将带有相应参数的构造函数添加到您希望稍后实例化的类,并让它实现您创建的接口,如下所示:

public class ViewController implements IViewController{

@AssistedInject
public ViewController(@Assisted int i){
    final String LANGUAGE_BUNDLE = "languageBundle";
    final String FXML = "View.fxml";

    try{
        ResourceBundle resourceBundle = ResourceBundle.getBundle(LANGUAGE_BUNDLE, Locale.GERMAN, this.getClass().getClassLoader());
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(FXML), resourceBundle, new JavaFXBuilderFactory());
        fxmlLoader.setController(this);
        Stage second = new Stage();
        second.setScene(new Scene(fxmlLoader.load()));
        second.show();
    }catch (IOException e){
        e.printStackTrace();
    }
    System.out.println("ViewController constructor called with: " + i);
}

以下是一些需要注意的事项:

  • 方法
  • 的注释“@AssistedInject”
  • 我们想要外部提供的参数的注释“@Assisted”
  • 我们手动为装载程序设置控制器(使用fxmlLoader.setController(this);)
  • 我不得不删除fxml文件中的控制器配置,所以fxml中没有“fx:controller”!

接下来,我们需要在我们想要实例化另一个类的类中添加一个变量:

@Inject
IControllerFactory controllerFactory;

我们可以在课堂上使用它:

controllerFactory.create(3)

注意:我们调用我们从未在ViewController类中实现的方法“create”! Guice知道它必须调用构造函数 - magic

作为最后一步,我们需要在GuiceModule中添加与上下文的连接,如下所示:

@Override
protected void configure(){
    install(new FactoryModuleBuilder()
            .implement(IPagingDirectoryViewController.class, PagingDirectoryViewController.class)
            .build(IPagingDirectoryControllerFactory.class));
}

注意我收到了错误:无法解析方法'实现java.lang.Class&lt;“接口类”&gt ;, java.lang.Class&lt;“实现类”&gt;'。这是因为我忘了让我的Controller类实现接口。

好的,这就是我如何运作的。 正如我所说,我对某些意见感到非常高兴!

此致,基督徒

答案 1 :(得分:2)

在您的模块配置中,您只需为FXMLLoader添加一个提供者方法,您可以在其中指定Guices&#39; injector.getInstance()&#39;作为装载程序的ControllerFactory。

@Provides
public FXMLLoader getFXMLLoader(com.google.inject.Injector injector) {
    FXMLLoader loader = new FXMLLoader();

    loader.setControllerFactory(injector::getInstance);
    return loader;
}

您现在要做的就是在模块配置的configure()方法中绑定ViewControllers。

    // for example:
    bind(ViewController.class);

确保控制器类正确绑定在fxml文件中。

fx:controller="your.package.ViewController"

现在您只需使用您的注入器来获取FXMLLoader的实例。