it-swarm.com.de

Wie kann man JavaFX-Controller mit demselben Modellobjekt initialisieren?

Szenario

Ich erstelle eine GUI, in der mehrere Ansichten auf dasselbe Modellobjekt verweisen. 

Was ich gewöhnt bin an 

Wenn in Swing alle Ansichten auf dasselbe Modell verweisen sollen, würde ich das Modell an den Konstruktor übergeben. 

Was ich gerade mache

In JavaFX übergebe ich das Modell, indem ich eine Setter-Methode in den Ansichten/Controllern (Menüleisten, Teilfenster, Registerkarten usw.) nach dem Laden jeder Ansicht/Controller habe. Ich finde das sehr klebrig und umständlich. Außerdem finde ich, dass es nicht funktioniert, da in bestimmten Situationen das Modell bereits in einem Controller vorhanden sein muss, bevor einige der Controller-Widgets initialisiert werden.

Lackluster-Alternativen

(Hinweis: Ich beziehe mich auf diese Stackoverflow-Fragen: 

  • Javafx 2.0 How-to Application.getParameters () in einer Controller.Java-Datei
  • Parameter JavaFX FXML übergeben
  • Mehrere FXML mit Controllern, Objekt teilen
  • Parameter an einen Controller übergeben, wenn eine FXML geladen wird )

  • Abhängigkeitsspritze

    • Ich habe diese Website angesehen, http://www.zenjava.com/2011/10/23/javafx-2-0-fxml-and-spring/ , und ich habe ein wenig in Google Guice nachgesehen , aber ich sehe keine Möglichkeit, jedem JavaFX-View/Controller das gleiche Modellobjekt zu geben. Es schien, als würde die Injektion für jede Ansicht/jeden Controller ein anderes Modell injizieren.
  • Speichern des Modellobjekts als öffentliche statische Variable

    • Dies ist eine Option, aber im Moment mag ich nicht die Idee, ein öffentliches statisches Modell so offen und verfügbar zu haben. Natürlich kann ich es zu einer privaten statischen Variable machen und Getter und Setter haben, aber diese Idee gefällt mir auch nicht sehr. 
  • Übergabe von Parametern vom Aufrufer an den Controller

    • Ich möchte, dass sich jeder Controller in seinen Konstruktor lädt, und ich möchte, dass jeder benutzerdefinierte Controller automatisch in seinen übergeordneten Controller eingefügt wird. Beispielsweise wird die Registerkarte Kartenübersicht folgendermaßen geladen:

      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();
          }
      }
      
    • Und der SingleGameSetup-Controller hat die Registerkarte Kartenübersicht automatisch in eine Variable eingefügt:

      public class SingleGameSetupController extends AnchorPane {
      
          @FXML private CardOverviewTab cardOverviewTab;
      
          // Rest of the class
      }
      
    • Der Teil der Datei, der die Registerkarte Kartenübersicht enthält, sieht folgendermaßen aus:

      <CardOverviewTab fx:id="cardOverviewTab" />
      
    • Auf diese Weise muss ich mich nicht darum kümmern, einen Controller manuell zu laden, aber ich habe immer noch das Problem, das Modell einzustellen.

  • Controller am FXMLLoader einstellen

    • Diese Option ähnelt der gewohnten, das Modell als Parameter an den Konstruktor zu übergeben, es besteht jedoch immer noch das Problem, dass Controller manuell mit dem FXMLLoader geladen werden. 
  • Ereignisbus

    • Ich habe nicht viel darüber gelesen, aber aus dem, was ich gelesen habe, scheint der Ereignisbus inaktiv und veraltet zu sein.
  • Singleton

    • Dies ist vergleichbar mit einem öffentlichen statischen Verweis auf das Modellobjekt, das Controller abrufen können, aber ich suche auch hier nach einer besseren Lösung. Außerdem möchte ich kein Singleton-Modell.

Was ich suche

Gibt es eine Möglichkeit, das Modellobjekt auf weniger umständliche Weise herumzugeben? Ich suche nach einer Möglichkeit, die so einfach ist, das Modell an einen Konstruktor zu übergeben, aber ich möchte nicht das manuelle Laden von Controllern über den FXMLLoader oder das Setzen des Modells nach dem Laden der Controller übernehmen. Vielleicht ist es am besten, wenn Sie einen Kurs zum Abrufen des Modells haben, aber ich frage nur, falls es einen besseren Weg gibt.

23
j will

Update

Neben afterburner.fx auch checkout Gluon Ignite :

Mit Gluon Ignite können Entwickler in ihren JavaFX-Anwendungen gängige Abhängigkeits-Injection-Frameworks verwenden, auch in ihren FXML-Controllern. Gluon Ignite erstellt eine gemeinsame Abstraktion über mehrere gängige Frameworks zur Abhängigkeitsinjektion (derzeit Guice, Spring und Dagger, wir planen jedoch, weitere hinzuzufügen, sobald die Nachfrage offensichtlich wird). Mit der vollständigen Unterstützung von JSR-330 macht Gluon Ignite die Verwendung der Abhängigkeitsinjektion in JavaFX-Anwendungen einfach.

Die Injektion von Modellobjekten in Steuerungen erfolgt ebenfalls über @Inject, ähnlich wie bei afterburner.fx.

Vorgeschlagener Ansatz

Da Sie anscheinend nach einem Framework für die Abhängigkeitsinjektion suchen, sollten Sie am besten das afterburner.fx-Framework verwenden.

afterburner.fx bietet eine Möglichkeit, Modellobjekte mit der Standardanmerkung Java @ Inject in Ihre JavaFX-Controller einzufügen.

Alternative Abhängigkeitsinjektionssysteme

Spring ist groß und kompliziert und sollte aufgrund seiner Komplexität nicht in Betracht gezogen werden, es sei denn, Sie benötigen viele andere Funktionen für Ihre Anwendung.

Guice ist viel einfacher als Spring und eine vernünftige Wahl, wenn Sie ein Abhängigkeitsinjektionsframework mit zahlreichen Funktionen wie Anbieterklassen benötigen. Aber vom Klang her brauchen Sie nicht alle Funktionen, die Guice bietet, da Sie lediglich die Möglichkeit haben möchten, einzelne Instanzen von Objekten in Ihrer Anwendung zu umgehen, ohne sie explizit nachzuschlagen.

Probieren Sie afterburner.fx aus und prüfen Sie, ob es Ihren Anforderungen entspricht.

afterburner.fx Beispielcode

Hier ist ein Beispiel für das Injizieren einer Modellinstanz (NotesStore) in einen Controller mithilfe von afterburner.fx. Das Beispiel wird direkt aus der afterburner.fx-Dokumentation kopiert.

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 ist eine grundlegende Beispielanwendung, die die Verwendung von afterburner.fx demonstriert. Es gab einige Probleme, die dazu führten, dass followme.fx aufgrund von Inkompatibilitäten mit Maven-Abhängigkeiten direkt aus der Box lief. Ich gabelte den Code und behebte einige Probleme, die mich daran hinderten, es aus der Box heraus zu verwenden .

Antworten auf zusätzliche Fragen aus Kommentaren

Sagen Sie aus dem NoteStore-Beispiel, ich muss nur die Abhängigkeit des Afterburner-Frameworks hinzufügen und @Inject in meine Modellvariable einfügen?

Nein, Sie müssen auch eine zugeordnete Klasse erstellen, die FXMLView erweitert, und diese mit einem neuen Aufruf instanziieren (ähnlich wie NotesListView im obigen Beispielcode erstellt wird). Wenn Sie das afterburner.fx-Framework weiter untersuchen möchten, verwenden Sie das followme.fx-Projekt als Basis, da es den vollständigen Quellcode für ein sehr einfaches ausführbares Beispiel unter Verwendung des Frameworks bereitstellt.

Ich habe Google Guice ausprobiert und es funktioniert. . . Im Konstruktor sehen Sie, dass ein Spieleinstellungsobjekt manuell injiziert wird.

Ich denke nicht, dass Sie den Guice-Injektor so manuell verwenden müssen. Ich denke, Sie können eine Controller-Factory einstellen auf einer FXMLLoader-Instanz die Injektion einleiten. So funktioniert das FXMLView in afterburner.fx. Das genaue Detail des in Guice verwendeten Injektionsmechanismus wird sich vom Afterburner.fx-Mechanismus unterscheiden, aber ich denke, das allgemeine Konzept der Einstellung der Reglerfabrik bleibt ähnlich.

Es gibt eine Demo der eingestellten Controller-Factory unter Verwendung von FXML und Guice in der Antwort auf: Verknüpfen von FXML und Controller in der Modulkonfiguration von Guice .

Es ist eine Schande, dass es keinen einfacheren Weg gibt, der Ihnen so viele Schwierigkeiten bereitet.

Als inkonsequente persönliche Randnotiz bin ich in Bezug auf das Thema der Abhängigkeitsinjektions-Frameworks im Allgemeinen ein bisschen ambivalent. Sicher, sie können helfen, aber oft bin ich für einfache Dinge mit einem Singleton mit einer getInstance-Methode und nicht mit dem ausgefeilteren Framework einverstanden. Trotzdem sehe ich, wie nützlich sie in größeren Projekten sein können und sicherlich in bestimmten Java Frameworks sehr beliebt sind.

16
jewelsea

Ich habe dieses Problem erforscht und herausgefunden, dass Abhängigkeitsinjektion zusammen mit einem Singleton (für das Modell) mein optimales Design ist. Ich habe gelernt, dass das Setzen einer Setter-Methode für das Modell in jedem meiner Controller eine Form der Abhängigkeitseingabe ist, ebenso wie das Modell durch den Konstruktor geleitet wird. Frühling und Guice sind die Rahmenbedingungen, um dem zu helfen. 

Ich habe versucht, Spring wie hier angegeben zu verwenden: http://www.zenjava.com/2011/10/23/better-controller-injection/ aber ich stieß auf das Problem, als die Konfigurationsklasse versuchte, einen Controller zu laden. Die Member-Controller konnten nicht geladen werden. Dies war vielleicht keine Frühjahrsthema, aber ich dachte mir, dass es mir nicht genug interessierte, die Zeit zu investieren, um es zum Laufen zu bringen. Außerdem hätte ich alle meine Controller-Dateien durchgehen und die Art und Weise bearbeiten müssen, wie sie erstellt wurden.

Nachdem ich herumgesucht und viel gelesen habe, fand ich heraus, dass dieser Artikel am besten zum Ausdruck bringt, was ich gerne tun würde: https://forums.Oracle.com/thread/2301217?tstart=0 . Da sich der Artikel auf Verbesserungsvorschläge bezieht, sind diese Verbesserungen in JavaFX noch nicht vorhanden. Aber wenn, dann werde ich sie umsetzen. Ein Beispiel: In der Lage sein, ein Modell über die fxml zu injizieren:

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

Das DataFX Framework verfügt über eine neue API namens DataFX-Flow. Mit dieser API können Sie einfach Objekte in einen View-Controller einfügen. Andere als Afterburner.fx-Bereiche werden hier unterstützt. Ein Beispiel finden Sie hier: http://www.guigarage.com/2013/12/datafx-controller-framework-preview/

0
Hendrik Ebbers