Beliebte Suchanfragen
//

Integration von JavaFX in Swing-Applikationen

28.6.2015 | 5 Minuten Lesezeit

Eines vorweg: Bei Überlegungen, einen bestehenden Swing-Client in Richtung JavaFX zu migrieren, sollte dringend die Option geprüft werden, eine von Grund auf neue JavaFX-Applikation zu entwickeln und nicht Swing mit JavaFX (oder umgekehrt) zu vermischen!

Sollen dennoch JavaFX-Inhalte in eine Swing-Applikation eingebettet werden (ist mir in der Praxis durchaus schon über den Weg gelaufen), steht im Package javafx.embed.swing die Klasse JFXPanel bereit. Ein JXFPanel ist vom Typ javax.swing.JComponent abgeleitet und kann somit einfach direkt in Swing verwendet werden.

Swing – JavaFX – Interoperability

Swing und JavaFX sind jeweils Single Threaded UI Toolkits, wobei der (AWT) Event Dispatch Thread (EDT) für Swing automatisch mit der JVM gestartet wird, während der JavaFX Platform Thread explizit gestartet werden muss.

Praktischerweise macht dies der Konstruktor des JFXPanel durch initFX() gleich mit:

Aus dem EDT können JavaFX-Controls aber nur über den JavaFX Platform Thread angesprochen werden. Dazu wird ein Runnable über die Klasse javafx.application.Platform.runLater(Runnable run) eingereicht. Ein Versuch, aus einem anderen Thread heraus, z. B. den Text von einem javafx.scene.control.Label zu ändern, wird mit einer
java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
geahndet!

Hier sind zur Verdeutlichung zwei funktional identische Panels in Swing und JavaFX, jeweils mit einem Button, einem TextField und einem Label erstellt. Beide Panels sind in ein javax.swing.JFrame eingebettet und können jeweils den Inhalt des Textfeldes an ein Label der „Gegenseite“ übergeben:

Mit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von YouTube.
Mehr erfahren

Video laden

YouTube immer entsperren

Und hier ist der Code für das JFXPanel:

Vor Java 8u40 war es nötig, die javafx.scene.Scene via Platform.runLater() zu erzeugen, denn die Scene hat im Konstruktor via Toolkit.getToolkit().checkFxUserThread() andernfalls für einen Abbruch gesorgt. Seit 8u40 wird dieses nicht mehr so streng gehandhabt und es ist erlaubt, eine Scene auch direkt aus dem EDT heraus zu erstellen.

Der Code für das SwingPanel…

… und für das Demo-Fenster:

Man beachte den ActionListener, der am JButton registriert wird: Bei der Interaktion muss unbedingt darauf geachtet werden, dass JavaFX Controls via Platform.runLater() über den JavaFX Platform Thread angesprochen werden.

FXML

Alternativ kann natürlich auch im JFXPanel die UI als FXML geladen werden. Damit profitiert auch ein Swing-Client von den Vorteilen einer deklarativen Oberfläche und von Tools wie dem SceneBuilder .

Die FXML-Datei gestaltet sich recht übersichtlich…

… und wird dann über den FXMLLoader geladen. Durch loader.setController(this) werden dann die mit @FXML annotierten JavaFX-Controls beim Laden durch den FXMLLoader injiziert:

Fallstricke

In der Praxis wird man vermutlich das JFXPanel nicht nur ein einziges Mal in der Applikation benutzen und bestimmt diese auch zur Laufzeit dynamisch erzeugen und wieder verwerfen.
Verwendet man zum Beispiel ein JFXPanel in einem JDialog wird, wie bereits erwähnt, der JavaFX Platform Thread vom JXFPanel gestartet, falls dieser nicht bereits läuft.
Allerdings sorgt das JFXPanel auch dafür, dass der Platform Thread beendet wird wenn die letzte Instanz von JFXPanel „stirbt“ (Platform.exit()). D.h. in diesem Fall wenn der JDialog beendet wird. Beim zweiten Instanziieren des Dialogs folgt dann eine Exception:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Platform.exit has been called

Denn: Der JavaFX Platform Thread lässt sich nicht erneut starten, wenn dieser einmal beendet wurde. Das JFXPanel bleibt in diesem Fall aus Benutzersicht einfach leer.

Dieses implizite Schließen des JavaFX Platform Threads kann aber recht einfach verhindern werden:
Platform.setImplicitExit(false)
schaltet diesen Automatismus ab. Am besten ist, diesen Aufruf bereits ganz oben in main() zu setzen. Der JavaFX Platform Thread wird dann erst durch den expliziten Aufruf von Platform.exit() beendet.

Fazit

Wie eingangs erwähnt, ist es nicht angeraten, Swing und JavaFX zu stark zu vermischen. Eine vollständige Neuimplementierung in JavaFX ist der ausdrücklich zukunftsfähigere und qualitativ hochwertigere Weg!

Im Übrigen stellt es einen nicht unerheblichen Aufwand dar, den Stil so anzupassen, dass der Benutzer eine konsistente Oberfläche erlebt. Vor allem vor dem Hintergrund, dass Swing in der Regel versucht das jeweilige native Look-and-Feel anzunehmen, JavaFX-UIs dagegen standardmäßig auf jeder Plattform (in etwa) gleich aussehen.

Es kann aber durchaus Anwendungsfälle geben, bei denen bis auf Weiteres der Swing-Client beibehalten werden soll und man aber trotzdem von JavaFX-Komponenten wie z. B. der WebView  oder JavaFX Media profitieren möchte.

Der obige Beispielcode ist HIER zu finden.

//
Jetzt für unseren Newsletter anmelden

Alles Wissenswerte auf einen Klick:
Unser Newsletter bietet dir die Möglichkeit, dich ohne großen Aufwand über die aktuellen Themen bei codecentric zu informieren.