Wie lässt sich JavaFX in Swing-Applikationen einbetten?
Die beiden UI-Frameworks verwenden jeweils eigene Threads
für die Ausführung ihrer Benutzeroberflächen. Bei
Swing ist dies der Event Dispatch Thread (EDT)
, bei
JavaFX wird er als Application Thread
bezeichnet.
Der Zugriff auf die jeweiligen Komponenten darf nur aus den
zugehörigen Threads heraus erfolgen. Hieraus erwächst
die Notwendigkeit, beide strikt getrennt zu halten. Wie
können sie dann wechselseitig aufeinander zugreifen?
Für die Einbettung von JavaFX in Swing wird die Komponente
JFXPanel
verwendet, die mittels setScene()
die gewünschte FX-Scene übergeben bekommt. Das Panel
ist von JComponent
abgeleitet und stellt somit
selbst eine Swing-Komponente dar, die gleichwohl zum Package javafx.embed.swing
gehört. Sie muss allerdings so eingebettet werden, dass die
Ausführung des UI auf dem Application Thread erfolgt. Dies
geschieht durch die Methode Platform#runLater(Runnable r)
.
Das Runnable r
wird hierbei, wie bei Threads
üblich, nach Übergabe an die Methode zu einem nicht
genau planbaren Zeitpunkt ausgeführt.
import javax.swing.JFrame;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.web.WebView;
import javax.swing.SwingUtilities;
public class FXInSwing {
private static final int WIDTH = 800;
private static final int HEIGHT = 500;
public FXInSwing() {
initGUI();
}
public void initGUI() {
final JFXPanel panel = new JFXPanel();
JFrame frame = new JFrame("JavaFX in Swing");
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(WIDTH, HEIGHT);
frame.setLocationRelativeTo(null);
Platform.runLater(() -> initJFXPanel(panel));
frame.setVisible(true);
}
private void initJFXPanel(JFXPanel panel) {
Scene scene = new Scene(new Browser(), WIDTH, HEIGHT);
panel.setScene(scene);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new FXInSwing());
}
}
class Browser extends Pane {
final WebView view = new WebView();
public Browser() {
view.getEngine().load("https://yourwebs.de");
getChildren().add(view);
}
@Override
protected void layoutChildren() {
double w = getWidth();
double h = getHeight();
layoutInArea(view, 0, 0, w, h, 0, HPos.CENTER, VPos.CENTER);
}
}
Das Beispiel zeigt, wie eine Webseite mittels JavaFX innerhalb
einer Swing-Anwendung dargestellt werden kann. Der Autor
bevorzugte diese Lösung, als er bei der Entwicklung einer
Hilfeseite für eine ältere Applikation die Darstellung
einer zeitgemäßen, lokalen HTML-Datei benötigte
und bekanntermaßen vor der Tatsache stand, dass in der
Swing-Komponente JEditorPane
lediglich HTML3 und
nur rudimentäre Stylesheets unterstützt werden.
Die Klasse FXInSwing
erzeugt mit einem JFrame
eine Swing-Oberfläche als
Hauptfenster der Anwendung. Im Konstruktor wir die Methode initGUI()
aufgerufen, in der die Konfiguration des Fensters vorgenommen
wird. Bevor dieses sichtbar gesetzt wird, wird ein
Lambda-Ausdruck aufgerufen, der die Methode initJFXPanel()
ausführt. Dort wird die FX-Scene erzeugt und auf das
übergebene JFXPanel
gesetzt. Wie oben
erläutert greift Platform#runLater()
auf den
FX-Application-Thread zu, in dem die Scene ausgeführt
werden kann.
Man erkennt hier, dass die Deklaration und
Initialisierung des JFXPanel
-Objektes problemlos
auch auf dem EDT unter Swing erfolgen kann. Lediglich die
Ausführung des UI muss im Application Thread
erfolgen.
Der Lambda-Ausdruck ist erst ab Java 8 möglich, bei Verwendung einer früheren Version muss der Einzeiler durch die folgende Code-Stuktur ersetzt werden.
SwingUtilities.invokeLater(new Runnable() { @Override public void run() { initJFXPanel(panel); } });
Wie erwähnt wird innerhalb von initJFXPanel()
die FX-Scene an das Panel übergeben. Sie wird hier mit
einem Objekt vom Typ Browser
erzeugt, das wiederum
von Pane
, einer der Basisklassen für
Layoutflächen in JavaFX, abgeleitet ist.
In der Klasse
wird eine WebView
erzeugt, die dem Pane
hinzugefügt wird. Über die Methode getEngine()
wird die WebEngine der View angesprochen, die dann durch load()
den String der Seiten-URL als Parameter übergeben bekommt.
Quellen
https://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/overview.htm#JFXWV135
https://jaxenter.de/wie-man-swing-anwendungen-mit-javafx-modernisiert-25212
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.