2016-03-22 27 views
2

Heute habe ich Guice zu meiner Java FX Application hinzugefügt. Das Hauptziel bestand darin, die Singletons, die ich hatte, durch Injection zu ersetzen und Abhängigkeiten aufzubrechen. Bisher war alles in Ordnung, das ist der Code ich eine neue Szene zu beginnen haben:Daten mit Guice in JavaFX ViewController einspeisen

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); 
     } 
    }; 
} 
} 

Mein Guice Modul sieht wie folgt aus:

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

Bitte beachten Sie, dass ich die Pfade für die Ressource ersetzt Bündel und die fxml-Datei, da sie meine Identität ergeben hätten. Aber laden und alles funktioniert, also sollte das kein Problem sein;)

Jetzt ist das Problem, dass ich eine neue Ansicht mit einem Klick in eine andere Ansicht instanziieren möchte. Die zweite Ansicht sollte eine detailliertere Version der Daten in Sicht 1 anzeigen. Alles, was ich an die zweite Ansicht übergeben muss, ist eine Ganzzahl (oder int), aber ich habe absolut keine Ahnung, wie das geht.

Ich habe den Standard-FX-Setup mit:

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

Die ViewController bindet dann an Eigenschaften durch die ViewModel angeboten. Ich brauche das Int, um das richtige Modell zu wählen.

Alles, was ich finden konnte, war über die Annotation @Named, aber soweit ich sehen kann, wäre dies nicht verwendbar, um dynamische Daten zu injizieren.

Könnten Sie mir bitte einen Hinweis geben, was das, was ich tun möchte, heißt? Oder kurz gesagt: Wie kann ich eine Variable, ausgewählt von einer anderen Ansicht, in eine zweite ViewController injizieren und den Rest in der Standard-FX-Weise lassen?

Jede Hilfe geschätzt und danke im Voraus!

Grüße, Christian

Antwort

3

um ein bisschen mehr Nach dem Versuch, scheint es, wie ich eine Lösung von mir gefunden! Allerdings fühlt es sich "hässlich" an, was ich tue, also hätte ich gerne eine Bestätigung;)

Zuerst die Theorie: Guice unterstützt "AssistedInject". Dies ist der Fall, wenn eine Klasse nicht von einem Standardkonstruktor erstellt werden kann. Um AssistedInject verwenden zu können, müssen Sie die Erweiterung herunterladen (ich habe das Jar von maven repository heruntergeladen).

Was AssistedInject für Sie tut, ist, dass Sie eine Factory angeben können, die die Variable für Sie erstellt. Also hier ist das, was ich getan habe:

Erstellen Sie zunächst eine Schnittstelle für die Klasse, die Sie später verwenden wollen, in meinem Fall:

public interface IViewController { 
} 

Zweitens schaffen eine Schnittstelle für die Fabrik.Wichtig: Sie nicht die Fabrik implementieren

public interface IControllerFactory { 

    ViewController create(@Assisted int myInt); 

} 

Drittens den Konstruktor mit den entsprechenden Parametern Ihrer Klasse hinzufügen, die Sie später instanziiert wollen, und lassen Sie es die Schnittstelle implementieren Sie wie so erstellt :

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); 
} 

Hier sind ein paar Dinge zu beachten:

  • Die Anmerkung " @AssistedInject“für die Methode
  • Die Anmerkung‚@Assisted‘für den Parameter, den wir von außen liefern wollen
  • wir den Controller für den Lader manuell eingestellt (mit fxmlLoader.setController (this);)
  • I hatten um die controller-konfiguration in der fxml-datei zu entfernen, also kein "fx: controller" in der fxml!

Als nächstes müssen wir eine Variable in die Klasse hinzuzufügen, von wo aus wir die andere Klasse instanziiert wollen:

controllerFactory.create(3) 

:

@Inject 
IControllerFactory controllerFactory; 

Wir haben es in der Klasse wie so verwenden können, Hinweis: Wir nennen die Methode "create", die wir nie in der ViewController-Klasse implementiert haben! Guice weiß, es hat den Konstruktor aufzurufen - magic

Als letzten Schritt müssen wir die Verbindung zu unserem Zusammenhang in unserem GuiceModule hinzufügen, etwa so:

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

Hinweis ich den Fehler bekam: Methode kann nicht aufgelöst werden 'implementieren java.lang.Class < "Die Interface-Klasse">, java.lang.Class < "Die implementierende Klasse">'. Das war, weil ich vergessen habe, meine Controller-Klasse die Schnittstelle implementieren zu lassen.

Okay, so habe ich es funktioniert. Wie ich schon sagte, würde ich mich über einige Meinungen sehr freuen!

Grüße, Christian

2

In Ihrer Modulkonfiguration können Sie einfach eine Provider-Methode für FXMLLoader hinzufügen, in dem Sie Guices 'injector.getInstance()' als Controller für die Lader zuweisen.

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

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

Alles, was Sie jetzt tun müssen, ist Ihren Viewcontrollers in dem configure() -Methode der Modulkonfiguration zu binden.

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

Und stellen Sie sicher, dass die Controller-Klasse ordnungsgemäß in Ihrer fxml-Datei gebunden ist.

Jetzt verwenden Sie einfach Ihren Injektor, um eine Instanz von FXMLLoader zu erhalten.