© mark stay/Shutterstock.com
Migration von Swing auf JavaFX: ein Tutorial unter Einbezug des MVC-Patterns

Des Kaisers neue Kleider


Obwohl JavaFX 2.0 bereits 2011 [1] erschienen ist, verfügen etliche Java-Anwendungen noch über eine Java-Swing-Oberfläche, so auch die Back-up-Software Areca oder MediathekView, die Sendungen der öffentlich-rechtlichen Rundfunkanstalten zum Download anbietet. Da Oracle JavaFX als Nachfolger von Swing betrachtet, zeigen wir, wie ein Umstieg auf JavaFX erfolgen kann. Das Tutorial berücksichtigt das Model-View-Controller-Pattern (MVC), auf dem Anwendungen mit grafischer Oberfläche üblicherweise basieren.

2018 gab Facebook bekannt, den Datenzugriff auf Facebook einzuschränken. Anwendungen von Drittanbietern sollen nur noch auf das Facebook API zugreifen können, wenn Facebook und ein Administrator es erlauben [2]. Aktuell wird über die Einschränkung des Internets diskutiert, weil durch Ausgangsbeschränkungen Mediatheken Hochkonjunktur erleben und es zu sogenannten Datenstaus kommt. Nicht mehr alle Informationen sollen für die Massen zugänglich sein. Es lohnt sich, in Anwendungen für mobile Geräte und für Computer zu investieren, die nur nach Log-in funktionieren.

Android-Apps und etliche Computerprogramme werden in Java programmiert. Die meisten denken bei Java-Anwendungen nur an öde aussehende Programme, basierend auf Swing. Dabei lassen sich dank der Einbindung von CSS nicht nur Anwendungen mit einer umwerfenden grafischen Oberfläche erstellen, sondern sogar Spiele animieren. Weitere Features von JavaFX [3]:

  • JavaFX gibt es schon seit einiger Zeit als Add-on-Paket für die Java-Entwicklungsumgebung, es ist aber erst seit Java 8 fest inkludiert. Ihr könnt JavaFX also auf allen Geräten laufen lassen, die JDK 8 oder höher unterstützen: z. B. Smartphones, Tablets, Computer.

  • Oracle betrachtet JavaFX als den Nachfolger von Swing und implementiert daher neue Features ausschließlich für JavaFX.

  • JavaFX nutzt Begriffe aus der Welt des Theaters, um die Features zu beschreiben. Die Stage (Bühne) stellt den obersten Container dar und kann sich über die gesamte Anzeigenfläche erstrecken. Bei einem Theaterstück wechseln die Szenen, doch ist immer nur eine Szene gleichzeitig zu sehen. So ist es auch bei JavaFX: Die Stage beinhaltet mehrere Scenes, doch kann nur eine Scene zur selben Zeit aktiv sein.

  • Die Komponenten eines Anwendungsfensters wie Buttons, Checkboxen oder Labels werden in einer Scene gespeichert. Sie werden Nodes (Knoten) genannt. Auch Layoutmanager und geometrische Formen gehören zur Klasse der Nodes.

  • JavaFX unterstützt das Konzept von Properties. Eine Property ist eine Variable, deren Wert sich beobachten lässt. Ein Listener wird an eine Property gehängt, und sobald sich der Wert der Variable ändert, wird ein Ereignis ausgelöst. Mögliche Folgen können z. B. die Änderung der Farbe einer Form sein.

  • JavaFX-Anwendungen lassen sich mit CSS layouten. Genauso wie bei Webanwendungen werden die Stildefinitionen in einer Datei mit der Endung .css gespeichert. Darüber hinaus können CSS-Klassen oder -IDs in einer .java-Datei definiert werden.

  • JavaFX bietet Spezialeffekte an, die auf jedem Knoten innerhalb der Scene angewandt werden können. Mit visuellen Effekten wie Unschärfen, Schatten oder Reflexionen lässt sich das Aussehen von Benutzeroberflächen verändern.

  • Die integrierte Unterstützung von Animationen ermöglicht es, Effekte wie Überblendungen oder Rotationen an einem x-beliebigen Knoten durchzuführen. Mit den KeyFrame- und Timeline-Klassen lassen sich benutzerdefinierte Animationen einrichten.

  • Spezialklassen erlauben das Bedienen der JavaFX-Anwendung auf Geräten mit Touchdisplay.

Abstecher zu Swing

Da vielen Unternehmen eine Anpassung ihrer Anwendung an JavaFX als zu umständlich erscheint, ist Swing sehr verbreitet [4]. Unter Swing erstellt man zunächst ein leeres Fenster, genannt Frame, das durch die JFrame-Klasse definiert wird. Dem leeren Fenster kann ein Panel der Klasse JPanel, das alle Komponenten der Benutzeroberfläche enthält, hinzugefügt werden. Eine Swing-Anwendung ist im Grunde eine Klasse, die die JFrame-Klasse erweitert. Anders als in JavaFX, wo es mehrere Layoutmanager gibt, existiert unter Swing nur das JPanel, dessen Layout ihr festlegen könnt. Events in Swing sind weniger durchdacht und nicht so konsistent wie in JavaFX. Swing besitzt grundlegende Steuerelemente: Schaltflächen, Kontrollkästchen, Kombinationsfelder und Ähnliches. Das Einrichten von Animationen ist unter Swing weniger komfortabel. Sowohl der Timer als auch die Logik müssen selbst implementiert werden. Swing unterstützt keine Touchdisplays. Swings JOptionpane-Klasse erlaubt es, kurze Warnmeldungen anzuzeigen oder einfache Eingaben vom Benutzer zu erhalten. So etwas existiert bei JavaFX nicht.

Das MVC-Pattern

In der Softwareentwicklung wendet man Patterns an, um eine Anwendung zu bauen. Das MVC-Pattern (Abb. 1) unterteilt eine Anwendung in drei miteinander verbundene Teile. Das Hauptziel des Patterns ist es, die interne Darstellungen einer Anwendung von der Art und Weise zu trennen, wie dem Benutzer Informationen präsentiert werden. Diese drei Komponenten bestimmen das MVC-Pattern [5]:

  1. Das Model managt die Daten. Es verwaltet Listen, Zustände und gegebenenfalls die Datenbank. Obwohl das Model sich der View und des Controllers nicht bewusst ist, stellt es eine Schnittstelle zur Verfügung, um Zustände zu manipulieren und abzurufen [6].

  2. Die View repräsentiert das Model. Die erforderlichen Zustände und Daten werden vom Model übermittelt.

  3. Der Controller beinhaltet die Logik der Anwendung. Er ist für das Event Handling zuständig.

Sobald der Anwender Aktionen an der View vornimmt, verarbeitet der Controller die Events und aktualisiert die Zustände des Models. Daraufhin schickt das Model die neuen Daten zur View.

minosi_javafx_1.tif_fmt1.jpgAbb. 1: MVC-Pattern

Richtige Version von JDK und JavaFX

Zum Zeitpunkt der Niederschrift des Artikels können Windows- und Linux-User Version 13 der Java-Entwicklungsumgebung herunterladen [7]. OpenJDK gibt es immerhin in der Version 14 [8]. Nutzer des Raspberry Pi brauchen die Edition für Java Embedded, die jedoch nur in Version 8 verfügbar ist [9]. Java Embedded beinhaltet allerdings keine Unterstützung für JavaFX, sodass man JavaFX manuell installieren muss [10].

JavaFX unter Java Embedded aktivieren

Nach dem Entpacken der .zip-Datei kopiert ihr die JavaFX-Bibliotheken in den Ordner der Java-Embedded-Installation (Tabelle 1). Außerdem fehlt in der Java Embedded Edition die Klasse SwingFXUtils, die jedoch wichtig ist, sofern Dateien in die JavaFX-Anwendung geladen werden. Unter [11] könnt ihr die fehlende Klasse herunterladen.

Pfad JavaFX ARM

Pfad Java Embedded

~/armv6hf-sdk/rt/lib/ext/jfxrt.jar

~/jdk1.8.0_xyz/jre/lib/ext/

~/armv6hf-sdk/rt/lib/arm/*

~/jdk1.8.0_xyz/jre/lib/arm/

~/armv6hf-sdk/rt/lib/javafx.platform.properties

~/jdk1.8.0_xyz/jre/lib/

~/armv6hf-sdk/rt/lib/javafx.properties

~/jdk1.8.0_xyz/jre/lib/

~/armv6hf-sdk/rt/lib/jfxswt.jar

~/jdk1.8.0_xyz/jre/lib/

Tabelle 1: JavaFX unter Java Embedded aktivieren

Vorstellung der Beispielanwendung

Alle hier vorgestellten Java-Snippets wurden mit der Java Embedded Edition JDK 1.8.0_231 erstellt. Sie lassen sich auch mit den Java-Versionen für die x86-64-Prozessoren kompilieren. Die Anwendung, ein Programm mit einer grafischen Oberfläche, wurde für ein fiktives Burgerrestaurant programmiert. Das Hauptmenü wird in Form von vier Tabs dargestellt, die das Startfenster (Abb. 2), ein Fenster zur Darstellung der Bestellungen (Abb. 3), die Artikelansicht (Abb. 4) sowie die Warenkorbansicht (Abb. 5) beinhalten. Klickt der Benutzer auf den Orders-Tab, wird ein Ereignis ausgelöst, das die Bestellungen anzeigt. Diese werden in einer Liste festgehalten, die aus den Attributen Bestellnummer, Bestelldatum, den bestellten Menüs, Endpreis und Name des Bestellers besteht. In einer echten Anwendung stammte die Liste aus einer Datenbank, aber für dieses Beispiel wird eine Liste in der Testklasse TestLists angelegt. Der User kann im Drinks-Tab Getränke in den Warenkorb legen, der im Basket-Tab abgerufen wird.

minosi_javafx_2.tif_fmt1.jpgAbb. 2: Startfenster der Burgeranwendung (Quelle: Pixabay/Niek Verlaan)
minosi_javafx_3.tif_fmt1.jpgAbb. 3: Im Orders-Tab werden die Bestellungen angezeigt
minosi_javafx_4.tif_fmt1.jpgAbb. 4: Eine Liste von Getränken wird im Drinks-Tab zum Verkauf angeboten
minosi_javafx_5.tif_fmt1.jpgAbb. 5: Auf den Warenkorb kann über den Basket-Tab zugegriffen werden

Das Modell setzt sich u. a. aus den Enums Hamburger und Drink zusammen (Abb. 6, Listing 1). Sie beinhalten nicht nur die Attribute wie Name und Preis, sondern auch die nötigen Setter- und Getter-Methoden. Ein Menü kann sowohl aus einem Hamburger und einem Getränk als auch lediglich aus einem Hamburger oder einem Getränk bestehen. In der Menu-Klasse befinden sich nicht nur die erforderlichen Attribute, sondern der Menüpreis, der extra berechnet wird. Eine Bestellung wird erzeugt, indem eine Instanz der Order-Klasse angelegt wird. Die Order-Klasse enthält die Attribute Person, Liste der Menüs, Bestelldatum, Bestellnummer sowie den Gesamtpreis. Eine Person kann mehrere Menüs bestellen, die in einer Liste vom Typ Menu gespeichert werden. Zum Berechnen des Gesamtpreises iteriert eine Setter-Methode durch die Menüliste und addiert die Preise der einzelnen Menüs. Vor- und Nachname des Kunden werden in der Person-Klasse festgehalten.

Listing 1

public class ModelServer {  // Locale wird zum Ermitteln der richtigen Währung benötigt private Locale locale; private Currency c; private ObservableList<Order> data; private ObservableList<Person> persons; private ArrayList<Menu> menus; private Stage primaryStage = null; public ModelServer(Stage primaryStage,Locale locale) { this.primaryStage = primaryStage; this.c = Currency.getInstance(locale); this.data = FXCollections.observableArrayList(); this.persons = FXCollections.observableArrayList(); this.menus = new ArrayList<Menu>(); } public Stage getPrimaryStage() { return primaryStage; } public ObservableList<Order> loadData() { return this.data; }  // formatiert das Datum public String getDateTime(String pattern) { DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern); LocalDateTime now = LocalDateTime.now(); return dtf.format(now); } public Locale getLocale() { return this.locale; } public String getCurrency() { return this.c.getCurrencyCode(); } public void addOrder(Order o) { if (!this.data.contains(o)) { this.data.add(o); } }  // Bestellung zur Liste hinzufügen public void addOrder(Person p, ArrayList<Menu> l) { String date = getDateTime(Constants.DATETIME2); Order o = new Order(p, l, date, getCurrency() ); addOrder(o); }  // Menü zur Menü-Liste hinzufügen public void addMenu(List<Menu> l,Menu m) { if (!l.contains(m)) { l.add(m); } } public void addMenu(List<Menu> l,Hamburger h, int ah, Drink d, int ad ) { Menu m = new Menu( h,ah, d, ad ); addMenu( l,m); }  // eine Person zur Liste hinzufügen public void addPerson(String f, String l) { Person p = new Person(f,l); if (!this.persons.contains(p)) { this.persons.add(p); } } [...] }

Die Klasse ModelServer verwaltet die Listen für Bestellungen, Menüs und Personen und stellt dazu Getter- und Setter-Methoden bereit. Außerdem gibt es hier Attribute bzw. Methoden, die von anderen Klassen benutzt werden, um z. B. das aktuelle Datum auszugeben oder die Währung des jeweiligen Landes zu ermitteln. Die TestLists-Klasse beherbergt Instanzen der Person-Klasse, die zur Liste der Pers...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang