Ein erstes “Hello World” von JavaFX mit Maven
https://oer-informatik.de/jfx_03_aufbau_des_scenegraphs
tl/dr; (ca. 4 min Lesezeit): Was passiert unter der Oberfläche in JavaFX? Was ist eine Scene, ein Root Node, ein branch node und ein Leaf node? Der Artikel ist Teil eines JavaFX-Tutorials.
Bühne frei: die Komponenten einer JavaFX-GUI
JavaFX gliedert den Aufbau einer GUI in begrifflicher Anlehnung an ein Theater. Wir gehen die Komponenten an Hand folgender Zeichnung durch:

Der Ort, an dem die GUI präsentiert wird ist die Stage, die Bühne des Theaters. Gemeint ist damit das Fenster (bei Desktop-Programmen), dass sich öffnet, wenn ich das Programm starte (oder der gesamte Programmbildschirm bei Tablet/Smartphone-Anwendungen).
Auf der Bühne wird eine Szene (Scene) abgespielt. Eine Szene hat eine bestimmte Größe und enthält den eigentlichen Aufbau. Es können bei Bedarf auch mehrere Szenen vorbereitet werden, die nacheinander auf die Bühne geholt werden.
Der Aufbau einer Szene besteht aus mehreren verschachtelten Elementen. Das erste Element, dass alle weiteren enthält, nennt man root node, den Wurzelknoten. Ein Wurzelknoten kann aus weiteren strukturierenden Verzweigungsknoten (branch node) bestehen (z.B. Listen, Tabellen, Stapeln), die ihrerseits weitere Knoten enhalten.
Die eigentlichen Inhalte (z.B. Texte, Bilder, Knöpfe, Grafiken) stellen Blätter dar (leaf node), die selbst keine weiteren Knoten enthalten können.
Neben dem Bild der Bühne wird für den verschachtelten Aufbau einer GUI oft ein zweites Bild bemüht: das eines Baums mit Wurzel/Stamm, Verzweigungen und Blättern. Das obige Beispiel lässt sich als Baum wie folgt darstellen:

Diese Baumstruktur wird Scenegraph genannt - ein vorwärtsgerichteter Graph mit Knoten (node) und Kanten, bei dem jeder Knoten genau einen Vorgänger hat.
Wie aber wird dieser Aufbau im JavaFX-Quelltext umgesetzt?
Anpassungen der Projektdateien
Das bereits angelegte JavaFX-Projekt verfügt über drei Dateien, die wir uns im folgenden genauer ansehen (und eine Reihe von Dateien, die wir am Ende löschen können, weil wir sie hierfür nicht benötigen):

App.java
ist die eigentliche Applikation - hier haben wir am meisten Arbeit vor unsmodule-info.java
- legt die Java-Abhängigkeiten festpom.xml
- konfiguriert das Gesamtprojekt
Der Name App.java
für die zentrale Datei, aus der heraus unser JavaFX-Programm startet, ist nicht festgeschrieben. Wir könnten den Namen in der pom.xml
(unserer Konfigurationsdatei) jederzeit ändern:
Im Paket de.csbme.ifaxx
sucht Maven nach einer Klasse de.csbme.ifaxx.App
(falls hier nur App
steht, kann es sein, dass der Compiler später mit Warnungen um sich wirft).
Maven startet in dieser Klasse eine public static void main(String[] args)
-Methode. Für das Grundgerüst benötigen wir also diese statische Methode.
Eine zweite Methode namens start()
benötigt JavaFX, um später unser eigentliches Programm aufzunehmen. Das Grundgerüst der App.java
sollte also etwa so aussehen:
package de.csbme.ifaxx;
/*Imports*/
public class App extends Application {
public void start(Stage primaryStage){
/*Implementierung*/
}
public static void main(String[] args) {
launch(args);
}
}
Bevor wir dieses Grundgerüst mit Leben füllen, sollten wir unsere Abhängigkeiten noch definieren. Um Javaprogramme schlank zu halten, sind in aktuellen Java-Versionen (>8) nur wenige Bibliotheken direkt im Sprachumfang von Java enthalten. Wir müssen daher in der Datei module-info.java
zunächst für unsere App festlegen, welche Module benötigt werden (requires: zwei JavaFX Module) und was von Außen genutzt werden darf (exports: unser komplettes Modul):
module de.csbme.ifaxx {
requires transitive javafx.graphics;
requires javafx.controls;
exports de.csbme.ifaxx;
}
Jetzt können wir die Platzhalter in der App.java
füllen. Die Methode start()
ist das Herzstück unseres ersten eigenen JavaFX-Programms. Hier können wir die oben genannten Grundlagen eins jeden JavaFX-Programms erkennen:
@Override
public void start(Stage primaryStage){
Text textLeaf = new Text("Hallo");
StackPane rootNode = new StackPane(textLeaf);
Scene scene = new Scene(rootNode, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
Die Stage erhalten wir als Parameter übergeben, von wem genau schauen wir uns später an. Die Stage ist der Ort, an dem alles angezeigt wird - das Startfenster unseres Programms. Wir schleichen uns von der letzten Zeile der Methode (primaryStage.show();
) nach oben.
Die Dinge, die wir auf der Bühne anzeigen nennen wir Scene - also Szenen (primaryStage.setScene(scene);
). In unserem Beispiel ist die Scene 300x200 Pixel groß und zeigt etwas an, das hier rootNode
genannt wurde (new Scene(rootNode, 300, 250);
).
Ein Node ist ein Inhaltselement, das weitere Inhaltselemente enthalten kann (z.B. eine Tabelle, ein Gitter, ein Stapel). In unserem Fall enthält der Stapel (StackPane
) nur ein Element, nämlich einen Text (new StackPane(textLeaf);
). Diesen Text haben wir in der ersten Zeile der Methode erstellt (Text textLeaf = new Text("Hallo");
).
Was jetzt noch fehlt, um ein lauffähiges Programm zu erhalten, sind die fehlenden Importe. Wenn es die IDE nicht alleine schafft, die korrekten Klassen zu finden, dann hilft vielleicht dieser Spickzettel zum vergleichen:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
Aufräumen im Programmcode…
In dem Beispielprogramm, das VSCode/Maven anlegt, sind schon einige Komponenten mehr enthalten, als wir am Anfang benötigen. Wir trennen uns von den Dateien, die wir nicht mehr benötigen:
Im Projekt liegen unter
\src\main\resources\de\csbme\ifaxx
die Dateienprimary.fxml
undsecondary.fxml
- die können gelöscht werden.Ebenso die beiden Klassen
PrimaryController.java
undSecondaryController.java
untersrc\main\java\de\csbme\ifaxx
Ein Blick unter die Oberfläche: Wer ruft hier eigentlich wen auf?
Es scheinen bei JavaFX wie von Zauberhand Dinge zu passieren, die schwer nachvollziehbar sind: In der main()
-Methode wird eine launch()
-Methode aufgerufen, die nirgends implementiert ist. Stattdessen gibt es eine start()
-Methode, die wiederum nirgends aufgerufen wird. Wir müssen hier kurz Licht ins Dunkel bringen, in dem wir uns mal ein UML-Klassendiagramm anschauen:

Das erste Geheimnis ist schnell gelüftet: die mysteriöse Methode launch()
-Methode, die in der main()
-Methode aufgerufen wird, ist eine statische Methode der Superklasse Application
, von der App erbt.
Das zweite Rätsel bleibt aber: start()
ist eine Instanzmethode. Aber an welcher Stelle wird eine Instanz von App gebildet? Und wo wird start()
aufgerufen?
Dazu werfen wir ein Blick in ein UML-Sequenzdiagramm, das die Abläufe im Hintergrund etwas erklärt:

Der Benutzer startet Maven, Maven wiederum ruft die
main()
-Methode der in derpom.xml
hinterlegten Klasse (hier:App
) auf. Das hatten wir ja bereits entschlüsselt.In dieser Main-Methode wird
launch()
aufgerufen, eine statische Methode der SuperklasseApplication
. Diese wiederum ruft eine statische Methode einer weiteren Klasse auf (muss man sich nicht merken, der Vollständigkeit halber: sie heißtLauncherImpl
):Der Dreh- und Angelpunkt ist die
launchApplication()
-Methode dieser mysteriösen Klasse (LauncherImpl
). Diese Methode erzeugt die Stage, auf der später alles angezeigt wird, instanziiert die KlasseApp
und ruft schlussendlich diestart()
-Methode auf, die dann wiederum die Stage selbst anzeigt (show()
). Bei Programmende wird hier noch die Methodestop()
derApp
aufgerufen.
Vielleicht war das für jetzt etwas dick aufgetragen. Ich komme jedoch häufig an den Punkt, an dem ich Dinge erst verstehe (und debuggen kann), wenn mir die dahinterliegende Struktur klar geworden ist. Wer noch etwas mehr über die Hintergründe und Realisierungen erfahren will, der kann sich den Quelltext der Superklasse Application sowie der Klasse LauncherImpl unter diesen Links ansehen.
Ausführen des JavaFX-Projekts
Wie immer können wir unser Programm direkt aus der IDE aufrufen, oder, indem wir die pom.xml
mit Maven addressieren:
Nächste Schritte
Dieser Artikel ist ein Teil der Artikelserie zu einer Energiemonitors mit JavaFX.
Als nächstes schauen wir uns die zugrunde liegenden Inhaltselemente branch nodes und leaf nodes mal etwas genauer an: Was sind Widgets, wie baue ich sie ein und wie hinterlege ich Aktionen?
Links und weitere Informationen
Quellen und offene Ressourcen (OER)
Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich in weiterbearbeitbarer Form im gitlab-Repository unter https://gitlab.com/oer-informatik/java-fx/ersteschritte und sind zur Nutzung als Open Education Resource (OER) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).