JavaFX-Entwicklung mit Eclipse (Installation veraltet!)

in Arbeit ...

Die vorliegende Unterlage soll die Hürden von Konfiguration und Einstieg in diese Themen beim autonomen Arbeiten möglichst gut bewältigbar machen.

Hier ist die Installation der benötigten Tools u.ä. sowie deren Handhabung beschrieben. Im Zweifelsfalle ist es empfehlenswert, alle Elemente auf den aktuellsten Stand zu bringen, da damit am wenigsten Probleme zu erwarten sind und durch die Homogenität der Infrastruktur bei allen Teilnehmern die Betreuung einfacher sein sollte.

Es wird nur Windows 10 behandelt – vermutlich verwendet in der Klasse niemand ein anderes System (falls doch, kann leicht Information dazu ergänzt werden)

Falls die Anleitung wichtige Fragen, Entscheidungen oder Probleme nicht behandelt, bitte ich um Rückmeldung – sie wird nach Möglichkeit ergänzt.

Für die Installation eines aktuellen JDK, der dazugehörigen API-Dokumentation (Javadoc) sowie eines aktuellen Eclipse
siehe Installation + Konfig. von JDK und Eclipse.

1. Installation und Konfiguration von JavaFX

1.1. Schaffen einer klaren Ordnerstruktur für Java-Komponenten

Da es eine große Zahl von Java-Zusatzbibliotheken gibt, von denen möglicherweise einige für die Projekte benötigt werden, sollte man einen sinnvollen Platz wählen, in dem all diese untergebracht werden. Ein guter Platz als Basis ist z.B.: C:\Users\<username>\Java, damit ergibt sich für JavaFX C:\Users\<username>\Java\JavaFX\ (u.U. Benutzer statt Users), wohin das extrahierte Verzeichnis javafx-sdk-17.0.1 und die ZIP-Datei mit Javadoc: openjfx-17-javadoc.zip zu kopieren ist.

1.2. Download und Installation

Die Pakete sind verfügbar unter https://gluonhq.com/products/javafx/.

Weiter unten können die gewünschten Pakete (SDK und Javadoc) für das passende Betriebssystem und die richtige Hardware-Architektur (x64) heruntergeladen werden.

Die Versionsnummern zwischen JDK und OpenJFX müssen nicht übereinstimmen - beide Projekte werden separat entwickelt, Kompatibilität ist in den OpenJFX Release-Notes nachzulesen - siehe Link auf https://gluonhq.com/products/javafx/.

2. Konfigurieren neue Java-Version und JavaFX

Öffnen Menü → Window → Preferences, darin in Suchfeld eingeben: "jre".

Im sichtbaren Teil-Baum auswählen: "Installed JREs", das in der Liste aufscheinende aktuelle JRE/SDK auswählen (oder mit add neue hinzufügen und danach auswählen)

JRE Name: temurin-jdk-17+jfx-17

JRE Home: /home/mxrenkin/WORK/SHARE/java/JDK/jdk-17.0.1+12

Default VM Arguments: --module-path ${JFX-17} --add-modules javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.media,javafx.web,javafx.swing

JRE System Libraries:j <javafx-dir>\lib\javafx.base.jar

2.1. Erstellen einer String-Substitution in Eclipse (derzeit nicht verwendet)

Um sich bei der Projekt-Konfiguration die Eingabe des Pfades zu den JavaFX-Bibliotheken zu ersparen, ist die Definition einer String-Substitution (eine Textersetzungs-Variable) empfehlenswert. Dazu öffnet man nochmals den Preferences-Dialog.
Darin: Run/Debug → String Substitution → New …​ . Im nun geöffneten Dialog die 3 geforderten Werte eingeben:
Name: JFX-17 (aus praktischen Gründen selber Name wie User Library)
Value: C:\Users\<username>\Java\OpenJFX\javafx-sdk-17\lib Description: JavaFX 17 LibraryOKApply and closeOK.

3. Installation und Test von e(fx)clipse: fällt derzeit weg!

Die JavaFX-Eclipse-e(fx)clipse Erweiterung (Version 3.6.0) ist in aktuellen Versionen von Eclipse/Java nur mehr teilweise funktionsfähig.
Es ist für die Erstellung von JavaFX-Projekten nicht nötig und erleichtert die Arbeit im derzeitigen Status insgesamt nicht wesentlich.

Nachteile des Weglassens:
  • die automatisch generierte JavaFX-Klasse Main, die ein leeres Fenster öffnet, muss händisch (sinnvollerweise durch Kopieren einer Vorlage) erstellt werden.

  • JavaFX-Properties müssen händisch erstellt werden.

  • vermutlich weitere.

Vorteile des Weglassens:
  • nicht funktionsfähige Bibliothek JavaFX SDK muss nicht mehr entfernt/verschoben werden

  • Es kann nicht irrtümlich ein erst händisch korrigierbares JavaFX-Projekt angelegt werden

  • Es gibt keine Probleme, die App "laufen zu lassen".

  • etc.

4. Erste Experimente mit JavaFX in Eclipse – Probe-Projekt

4.1. Probe-Projekt Überblick

Es wird nun ein normales Java-Projekt angelegt und die erzeugte User-Library JFX-15 eingebunden. Da JavaFX in Java 11 (und neuer) nicht mehr Teil des JDK ist, sondern unter Nutzung der neuen Modularisierungsmöglichkeiten bereitgestellt wird, ist die einfachste Lösung zur Einbindung, auch unsere JavaFX-Projekte als modularisiertes Java-Projekt zu organisieren.

Der wichtigste Schritt dazu ist das Erstellen einer Datei module-info.java direkt im Folder src des Eclipse-Projekts.

JavaFX in Java 11 nutzt ein neues, ergänzendes Strukturierungskonzept in Java-Projekten – Module.
Zusätzlich zu den Packages gibt es nun eine "darüber liegende" Struktur, die v.a. für große Projekte wesentliche Möglichkeiten bereitstellt.

Technisch muss dazu eine Datei direkt im Eclipse-Folder src angelegt werden – mit dem oben angeführten fixen Namen module-info.java.
Hier eine Beispiel-Datei – diese enthält für unsere Zwecke (JavaFX) nötige Einträge – Modul-Name my_unique.module und exportierter Package-Name my.unique.basepkg.proj_pkg sind anzupassen, u.U. weitere benötigte JavaFX-Module zu aktivieren:

Module-Descriptor in ${\proj_root_srcdir}: module-info.java
module my_unique.module {                (1)
    exports my.unique.basepkg.proj_pkg;  (2)
    //
    requires javafx.base;                (3)
    requires javafx.graphics;            (4)
    requires javafx.controls;            (5)
    //
    // requires javafx.media;            (6)
    // requires javafx.web;
    // requires javafx.fxml;
    //.
    // requires javafx.swing;            (7)
    // requires javafx.swt;
}
1 der Module-Name (Konventionen wie bei Packages – Kleinbuchstaben, Unterstriche, u.U. Punkte).
Die Punkte werden – anders als bei Packages – nicht in Subfolders "übersetzt".
2 das Package, ab dem das Mudul "beginnt"
3 das Basis-Package, das immer benötigt wird - es ist allerdings indirekt vorhanden – wird in den Modul-Descriptoren des jeweiligen Pakets als "requires transitive" gekennzeichnet. Bei Weglassen entsteht jedoch eine lästige Warnung.
4 ein benötigtes JavaFX-Modul ("requires transitive" würde bedeuten, dass die indirekten Abhängigkeiten – in diesem Fall javafx.base automatisch berücksichtigt würden, d.h. Zeile <3> könnte weggelassen werden)
5 ein zweites JavaFX-Modul (es gibt insgesamt 8)
6 weitere in späteren Projekten benötigte JavaFX-Module
7 die restlichen 2, vermutlich von uns in nächster Zeit nicht benötigten JavaFX-Module

4.2. Probe-Projekt Step-For-Step

4.2.1. Rohfassung estellen

  • Eclipse-Menü File → New → Project …​ → Dialog New Project öffnet → NORMALES Java Project (KEIN JavaFX-Projekt) → Projekt-Dialog öffnet sich.

  • Nur Projekt-Name - z.B. JfxTest1 eintragen, Execution Environment Use Default JRE …​

  • Next (noch nicht 'Finish'!) → im Dialog auf Tab: Libraries → In Liste obersten Eintrag: Modulepath selektieren !!

  • add Libraries …​ → in Liste: User Library → Next > → JFX-15 auswählen → Finish.
    Nun sollte in der Liste unterhalb von Modulepath der Eintrag JFX-15 zu sehen sein.

  • Nochmals Finish, um das Prolekt anzulegen.

  • Es erscheint erstaunlicherweise ein weiterer (nun wirklich letzter) Dialog: Create module-info.java mit Feld Module name:, Name eintragen (siehe anschließender "TIP"). Selektieren von Generate comments ist generell eine gute Idee.

Module sollten, falls 'öffentlich' nutzbar, global eindeutige Namen haben, aber in jedem Fall den Konventionen für Package-Namen folgen (KLeinbuchstaben + '_' + '.') – z.B.: inverser Domainname + interne Eindeutigkeit + Projekt.
Konkret: at.htlw5_1920.pos_2dhif_mustermann.jfxtest1
oder einfacher: mustermann_2dhif.jfxtest1
Oft wird empfohlen, Module exakt wie die Basis-Package des Moduls zu nennen.
  • Create klicken. Nun ist das Projekt vorhanden und die Datei module-info.java liegt direkt im Ordner src. Sie enthält außer dem Modul-Namen noch keine Information.

  • Klasse erzeugen, die den Startpunkt des Programms darstellt, Name häufig Main oder App. Am besten die Checkboxen fürs Erzeugen von public static void main(…​) und Constructors from super class abwählen, dann ist das Hineinkopieren des nachfolgenden Codes am einfachsten.

  • Nachstehende Methoden + Konstruktor direkt in die eben erzeugte Klasse hineinkopieren, Main von 'Application' erben lassen (wir damit zu einer JavaFX-Klasse) und die nötigen Importe (mit IDE-Unterstützung) durchführen (Details siehe nach dem Listing):

Klasse in ${proj_root_srcdir}: my.unique.pkg.Main.java
    public Main() {
        System.out.println("Constructor Main() called");
    }

    public static void main(String[] args) {
        System.out.println("main(...) called, now calling launch(...)");
        launch(args); // ruft indirekt start(...) auf
        System.out.println("main(...) finished");
    }

    @Override
    public void init() throws Exception { // Optional Initialisierung VOR Zugriff auf GUI (Dateien auslesen, etc.)!!
        super.init();
        System.out.println("init() - initialization (BEFORE access to GUI) called");
    }

    @Override
    public void stop() throws Exception {
        // Aufraeumen - MIT Zugriff auf GUI
        System.out.println("stop() called");
        super.stop();
    }

    @Override
    public void start(Stage primaryStage) throws Exception { // , bekommt das Hauptfenster als Parameter mit.
        // start(...) indirekt aufgerufen von launch(...), ihrerseits in main(...) aufgerufen
        System.out.println("start(...)- the GUI activities - called)");
        // Vom JavaFX Laufzeitsystem wird schon das Hauptfenster (hier 'primaryStage' genannt) bereitgestellt.
        // Erzeugen des Basis-Containers - entspricht in HTML ungefähr dem Bereich innerhalb <body>...</body>:
        BorderPane root = new BorderPane(); // Wurzel-Container für alle GUI-Elemente, wird unten 'scene' übergeben
        //
        // Scene ist der gesamte Window-Inhalt - vom Fenstertitel zu UI-Elementen wie Menü, Hauptbereich -
        // in Analogie zu Webseite entspricht es allem innerhalb von <html>...</html>.
        // Bei Erzeugung der Scene wird Basis-Container, Breite, Höhe des Fensters übergeben:
        Scene scene = new Scene(root, 300, 200);
        // Es können CSS-Stylesheets verwendet werden. Hier wird die aktuelle Stylesheet-Liste
        // abgefragt und eine eigene Stylesheet-Datei hinzugefügt (Pfad rel. zu Module-Basis-Dir):
        scene.getStylesheets().add(getClass().getResource("fx-app.css").toExternalForm());
        //
        // nun wird dieser Basis-Container dem Hauptfenster zugeordnet:
        primaryStage.setScene(scene);
        //
        // der Fenstertitel wird gesetzt (wie HTML-<title>):
        primaryStage.setTitle("Mein erstes JavaFX-Programm");
        //
        // Nun erstellen wir einige "richtige" UI-Elemente: ein Label, anschließend
        // einem der Container-Arten: VBox ...
        Label lbl1 = new Label("Hello User!"); // Erzeugen
        // Erscheinungsbild des Labels z.B. einfach mit CSS stylen - spezielle CSS-Namen mit Prefix '-fx-':
        lbl1.setStyle("-fx-font-size: 2em;-fx-background-color:yellow;");
        //
        // Erzeugen eines zweiten Containers (kann Untereinander-Anordnung seiner Elemente)
        VBox vbox1 = new VBox();
        vbox1.setStyle("-fx-border-color: red;"); // wieder stylen
        // lbl1 anfügen an Liste der 'children' von vbox1 ...
        vbox1.getChildren().add(lbl1);
        // noch minimale Größe für VBox festlegen (füllt verfügbaren Platz, wächst bei Bedarf automatisch):
        vbox1.setMinSize(250, 150); // Obergrenze: vbox1.setMaxSize(maxWidth, maxHeight);
        //
        // Auch vbox1 muss in den Basis-Container eingefügt werden. Dieser ist vom Typ BorderPane,
        // die 5 beschickbare Regionen hat: Top, Left, Center, Right, Bottom
        root.setCenter(vbox1); // hier wird im Center plaziert
        //
        // Zur Demo noch schnell ein Label im rechten Bereich:
        Label lbl4RootRight = new Label("hier\nist\nrechts!"); // \n ... Zeilenschaltung
        root.setRight(lbl4RootRight);
        //
        primaryStage.show(); // das erzeugte Fenster wird erst nach expliziter Aufforderung angezeigt.
        System.out.println("start(...) finished");
    }

4.2.2. Syntaxfehler-Bereinigung

Um die vielen Fehler zu beseitigen, ist es zuerst nötig, Klasse Main von JavaFX-Klasse javafx.application.Application erben zu lassen, d.h. umzustellen auf public class Main extends Application { …​ }.
Das könnte – und sollte – man schon im Eclipse-Wizard zur Klassenerstellung tun, womit schon zu Beginn ein syntaxfehlerfreier Source-Code entsteht – da man dies aber leicht vergisst, ist hier die daraus folgende Situation beschrieben.

Danach liefern die Eclipse-Quick-Fixes u.a. den Eintrag: Import 'Application' (javafx.application) and add 'requires javafx.graphics' to module-info.java – dieser ist zu wählen (genau schauen, es gibt einen ähnlichen mit (com.sun.glass.ui), der falsch ist!).
Achtung: nun NICHT bei Main auf Add unimplemented methods klicken, sondern nun alle vorgeschlagenen Importe in der gesamten Klasse durchführen (für Stage, BorderPane, Label, VBox, etc. – immer die Variante mit import '…​' (javafx.*), aber nie die mit javafx.swing.*).
Nun ist die Klasse syntaktisch fehlerfrei – weitere (beim Start auftretende) Fehler werden aber noch zu beseitigen sein.

4.2.3. Tipps zur Syntaxfehler-Bereinigung

Folgende Fehler können leicht auftreten:

Die JavaFX-Klassen werden nicht gefunden, Importe schlagen fehl

Kontrolle, ob die User-Library (bei uns JFX-15 genannt) im Package-Explorer sichtbar ist (eine Ebene unter dem Projekt, anschließend an src und JRE).

  • Wenn nicht: Angelegte User-Library JFX-15 zum Projekt hinzufügen:
    Rechtsklick auf Projekt im Package Explorer → Im Menu: Build PathConfigure Build Path …​ → im Dialog auf Tab: Libraries → analog zu Abschnitt 4.2.1, “Rohfassung estellen” vorgehen.

  • Wenn schon: User-Library ist vermutlich nicht korrekt konfiguriert - nochmals prüfen (siehe obigen Abschnitt [eclipse-userlib]).
    Die User-Library JFX-15 sollte hierarchisch "aufklappbar" sein, darunter sollten zumindest alle von uns benötigten JavaFX-JARSs sichtbar sein (entsprechen den im module-info.java mit requires gelisteten JavaFX-Modulen):

    • javafx.base.jar

    • javafx.controls.jar

    • javafx.graphics.jar

4.2.4. Startvorbereitung – Run Configuration erstellen

Zum Starten der App muss eine Run Configuration erstellt werden: → Rechtsklick auf das Projekt im Package-Explorer → unterster Eintrag: PropertiesRun/Debug SettingsNew …​Java ApplicationOK.
Im nun geöffneten Dialog in Tab Main zuerst sinnvollen Namen für die Runtime-Configuration wählen
– z.B.: JfxTest1_Main (<Projektname>_<App-Klassen-Name>).
Das richtige Projekt sollte schon ausgewählt sein (sonst unter Browse …​ auswählen). → den "Main-class"-Eintrag mit der eigenen Klasse unter Search …​ wählen (z.B. Main - mein.pkg) → OK. Im Tab Arguments ist nun keinerlei Eingabe erforderlich. → OK → nun sollte die neue Launch Configuration in der Liste sichtbar sein. → Apply and Close.

4.2.5. Erster Startversuch – Laufzeitfehler-1-Bereinigung

Erster Startversuch wirft Exception.
Die Fehlermeldung enthält (meine Klassen und Packages) die Zeile: Caused by: java.lang.IllegalAccessException: class com.sun.javafx​.application​.LauncherImpl (in module javafx.graphics) cannot access class rx1920_2dhif.jfxtest5.Main (in module rx1920_2dhif.jfxtest5) because module rx1920_2dhif.jfxtest5 does not export rx1920_2dhif.jfxtest5 to module javafx.graphics.

Daraus ist ersichtlich, dass das Modul javafx.graphics den 'export' meines Moduls erfordert. Somit ergänzen wir in Datei module-info.java den Eintrag (irgendwo innerhalb des Blocks module …​ {exports <strg><leerzeichen>} - und Eclipse bietet uns den richtigen Eintrag an – den Namen des eigenen 'module'. → Speichern, fertig.

4.2.6. Zweiter Startversuch – Laufzeitfehler-2-Bereinigung

Zweiter Startversuch: neuerliche Exception.
Der erste Fehler-Message aus einer eigenen Klasse ist: Caused by: java.lang.NullPointerException
at rx1920_2dhif.jfxtest5/rx1920_2dhif.jfxtest5.Main.start(Main.java:48)

Zeile Main.java:48 ist:
scene.getStylesheets().add(getClass().getResource("fx-app.css").toExternalForm()); Es sollte hier eine CSS-Datei geladen werden, die ist aber nicht da. Lösung: direkt 'neben' Klasse Main Datei fx-app.css erzeugen – mit dzt. nur einem Kommentar als Inhalt (sinnvoller Inhalt wird später besprochen), z.B.:
/* JavaFX CSS - Leave this comment until you have created at least one rule which uses -fx-Property */.

4.2.7. Dritter Start – Erfolg

keine Exception mehr – die erste JavaFX-App öffnet ihr GUI (vorerst keine weitere Funktion):

Das laufende JavaFX-Programm
Abbildung 1. Die laufende JavaFX-App

4.3. Mögliche Probleme beim Arbeiten an JavaFX-Apps

  • Falls der Modul-Namen geändert wird, muss die Run Configuration neu erstellt werden - wegen eines Bugs in Eclipse wird diese Änderung nicht automatisch übernommen und der Programmstart kann fehlschlagen mit der Fehlermeldung:
    Error occurred during initialization of boot layer
    java.lang.module.FindException: Module RX-JFX-Scribble2 not found

    Beim Neu-Erstellen einer Run Configuration wird der aktuelle Modul-Name jedoch korrekt verwendet und das Problem ist behoben.

  • Weiteres folgt bei Bedarf