Aus einer normalen Java Klasse eine FXML-Klasse laden

Jose05

Aktives Mitglied
Hallo,
ich möchte aus einer ganz normalen Java Klasse eine JavaFXML-GUI laden. Könnte mir jemand helfen?
Danke schonmal!
Code:
public class Main
{
    public static void main(String[] args)
    {
        new WillkommenFenster(); //Das funktioniert bei mir nicht.
    }
}
Java:
public class WillkommenFenster extends Application
{
    @Override
    public void start(Stage stage) throws IOException
    {
        FXMLLoader fxmlLoader = new FXMLLoader(WillkommenFenster.class.getResource("WillkommenFXML.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
        System.out.println("Hallo");
    }
}
 
Zumindest bei der Code-Standardvorgabe (schliesst Erweiterungen nicht aus) findet sich in der main-Methode der Code
Java:
 ..
...args) {
        launch();
    }
 
new WillkommenFenster(); //Das funktioniert bei mir nicht.
Das funktioniert schon - nur eben macht das halt nicht wirklich etwas. Du hast dann halt eine neue Instanz vom Typ WillkommenFenster. Aber das ist ja nichts, das Du sehen kannst.

Wenn Du eine JavaFX Anwendung hast, dann musst Du natürlich erst einmal alles initialisieren, das da notwendig ist. Dann muss ein Fenster erstellt werden und das musst Du dann füllen.

Wie @M.L. schon gesagt hat: Eine Möglichkeit ist die launch Methode. Diese macht diesbezüglich einiges. Es wird einiges initialisiert (damit das mit den Fenstern funktioniert braucht man eine Schleife, die dann halt Events abarbeitet und so) und dann ein Fenster (das ist die Klasse Stage) erzeugt. Und Application hat da ein paar Methoden, die aufgerufen werden, die man da überschreiben könnte.

Das kann man aber auch manuell machen. Wie das aussehen könnte sieht man dann z.B. in der Dokumentation von Oracle wenn man sich anschaut, wie JavaFX in einer Swing Anwendung integriert werden kann:

Da sieht man dann halt, dass da eine Scene erstellt und gefüllt wird. Wichtig ist: Das alles findet bereits in einem JavaFX psezifischen Event statt - dazu sorgt das Plattform.runLater.
 
Das Beispiel ist evtl. noch nicht so gut gewählt, da wir dort ja eine swing Anwendung und ein JFXPanel haben. Wenn das nicht gegeben ist, dann ist ein Platform.startup Aufruf wichtig und notwendig!

Die Dokumentation der Platform Klasse ist da eindeutig:

a) startup
This method starts the JavaFX runtime. The specified Runnable will then be called on the JavaFX Application Thread. In general it is not necessary to explicitly call this method, since it is invoked as a consequence of how most JavaFX applications are built. However there are valid use cases for calling this method directly. Because this method starts the JavaFX runtime, there is not yet any JavaFX Application Thread, so it is normal that this method is called directly on the main thread of the application.
This method may or may not return to the caller before the run method of the specified Runnable has been called. In any case, once this method returns, you may call runLater(Runnable) with additional Runnables. Those Runnables will be called, also on the JavaFX Application Thread, after the Runnable passed into this method has been called.
Also nicht aufzurufen, wenn man eine normale JavaFX Anwendung hat, die ja mittels launch() gestartet wird. Aber ansonsten ist es halt notwendig, ehe ein runLater aufgerufen wird. Aber nach dem Aufruf ist es dann sicher möglich. runLater zu nutzen.

b) runLater
This method must not be called before the FX runtime has been initialized. For standard JavaFX applications that extend Application, and use either the Java launcher or one of the launch methods in the Application class to launch the application, the FX runtime is initialized by the launcher before the Application class is loaded. For Swing applications that use JFXPanel to display FX content, the FX runtime is initialized when the first JFXPanel instance is constructed. For SWT application that use FXCanvas to display FX content, the FX runtime is initialized when the first FXCanvas instance is constructed. For applications that do not follow any of these approaches, then it is necessary to manually start the JavaFX runtime by calling startup(Runnable) once.
Also wenn man keine JavaFX Anwendung per launch startet und auch keine swing Anwendung mit JFXPanel hat (und auch kein SWT - das haben wir bsiher nicht betrachtet und lassen es jetzt auch), dann ist eben der Aufruf von startup notwendig!
 
Teil II: eine kleine Modifikation der Vorgabe (IntelliJ+JavaFX, anlegen von Main.java, auskommentieren der Main-Methode in HelloApplication.java, manuelles Einstellen der Configuration) und etwas Experimentieren führt zu folgendem Ergebnis:
IntelliJ_Main.JPG
 
Jetzt wäre interessant, was Du in der HelloApplication gemacht hast. Wenn kein launch, dann vermutlich Platform.startup und einfach eine Stage erstellt?
 
Nur um das selbst einmal zu zeigen: Es reicht also etwas aus wie:
Java:
    public static void startup() {
        Stage primaryStage = new Stage();
        try {
            Parent root = FXMLLoader.load(JavaFXApp.class.getResource("test.fxml"));

            primaryStage.setTitle("FXML Welcome");
            primaryStage.setScene(new Scene(root, 300, 275));
            primaryStage.show();
        } catch (IOException ex) {
            logger.error("IOException!", ex);
        }
    }

das dann gestartet wird mit Platform.startup(() -> startup());

Wichtig ist: Das geht nur, wenn wirklich noch nicht initialisiert wurde. Wenn JavaFX schon initialisiert wurde, dann wäre es ein Platform.runLater Aufruf.
 
... vielleicht sollte ich auch mal erzählen, wieso ich eigentlich frage:
Ich will mit JavaFx ein kleines TicTacToe Spiel programmieren. Ich will die GUI von der Logik trennen, um die Logik z. B. nochmal in einer Android App zu verwenden. Da ich mehrere Fenster erzeugen möchte, zunächst soll eine GUI erzeugt werden in der die Spieler die Namen eingeben können, danach wird die eigentliche 3x3 Matrix eingeblendet, danach wenn das Spiel zu Ende ist, ein Fenster, in dem die Spieler gefragt werden, ob sie nochmal spielen wollen, dachte ich es sei sinnvoll das ganze Spiel aus der Logik Klasse zu "steuern". Wenn die erste FXML GUI aufgerufen ist, rufen die Fenster sich gegenseitig auf, es muss nur das erste Fenster aus der Logik Klasse gestartet werden. Würdet ihr das auch so machen?
 
Dann machst Du aber dennoch ein ganz normales JavaFX Programm, d.h. Du hast eine Klasse, die von Application erbt und in der wird launch aufgerufen.

Und die Trennung macht Sinn, dabei aber aufpassen - dass Fenster sich gegenseitig aufrufen sollte man nicht unbedingt machen. Gegenseitige Abhängigkeiten sind nicht gut. Und Du willst Du eine separate Logik haben - die weiss dann doch, was gerade angezeigt werden soll und bekommt Eingaben. Da wäre dann so etwas zu entscheiden. Ein Fenster dient nur dazu, Dinge anzuzeigen und Eingaben entgegen zu nehmen.
 
Also ein Beispielprojekt mit mehreren Fenstern und so hat direkt einen entsprechenden Umfang. Sowas baut man nicht mal eben in 5 Minuten.

Schau Dir einmal die Pattern an, die es in der UI Entwicklung gibt. MVC und MVVM. Wobei ich zu MVVM raten würde. Da dürftest Du einiges finden. Startpunkt ist dann MVVMfx. Da findest Du extrem viele Links zu Dokumentation rund um das Pattern und auch einige Beispiele.

Und evtl. auch das, was Du machst, aus Sicht des Anwenders überdenken: Was für ein Verhalten willst Du programmieren? Ist das denn das Verhalten, das ein Anwender erwartet? Fenster die sich schließen und ständig neue Fenster, die aufgehen ist aus meiner Sicht eher unüblich. Das kenne ich so eigentlich nicht. Die einzige Applikation, die sowas macht, ist IntelliJ mit dem Startfenster. Das ist aber per Default auch nur beim ersten Start sichtbar - danach wird immer direkt das Hauptfenster geöffnet mit dem letzten Projekt. Da ist das eine Lösung, da es kein Projektfenster ohne Projekt gibt.

Es ist also eher üblich, ein Fenster zu öffnen und dieses dann immer zu nutzen. Und man kann dann noch zusätzlich Popups haben, also z.B. ein Fenster zum Login wenn der notwendig ist oder so. Da also ggf. einmal die diverse Software, die Du so nutzt, anschauen.

(Früher gab es auch Guides bei fast jeder Plattform, wie eine Anwendung sich verhalten sollte. Aber aktuell kenne ich sowas nicht mehr. Aber mal nach UI/UX Design suchen. Denn das ist natürlich weiterhin ein aktuelles Thema und das ist extrem wichtig. So haben viele Projekte auch direkt ein UI/UX Designer für das Frontend. Da kommen halt auch sehr viele Themen rein wie Barrierefreiheit und so.)
 

Zurück
Oben