12.13JavaFX-Scene in Swing-Applikationen einbetten
Obwohl JavaFX von Swing bzw. AWT vollständig entkoppelt ist, ist es doch nötig, beide Technologien zusammenzuführen. Für die nächste Zeit wird Swing (noch) die bevorzugte Plattform für Client-Applikationen bleiben, aber so wie Oracle im Moment die Richtung vorgibt, gibt es Innovationen ausschließlich in JavaFX und nicht in Swing oder AWT. Schon jetzt sind die Charts oder die Webkomponente aus JavaFX großartig und für Swing-Entwickler eine Bereicherung.
Es ist nicht kompliziert, den Szenengraphen in Swing-Anwendungen einzubetten. Im Mittelpunkt der Integration steht die Swing-Komponente JFXPanel, die einen JavaFX-Szenengraphen aufnehmen kann. Damit ist eigentlich schon alles gesagt:
Listing 12.20com/tutego/insel/javafx/JFXPanelDemo.java
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.embed.swing.JFXPanel;
import javafx.scene.*;
import javafx.scene.chart.PieChart;
import javax.swing.*;
public class JFXPanelDemo {
public static void main( String[] args ) {
JFrame f = new JFrame();
final JFXPanel fxPanel = new JFXPanel();
f.add( new JScrollPane( fxPanel ) );
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setBounds( 100, 100, 600, 600 );
f.setVisible( true );
Platform.runLater( new Runnable() {
@Override public void run() {
PieChart chart = new PieChart(
FXCollections.observableArrayList(
new PieChart.Data( "Java", 199 ),
new PieChart.Data( "Rest", 12 ) ) );
fxPanel.setScene( new Scene( chart ) );
}
} );
}
}
Das JFXPanel ist eine Swing-Komponente, die genauso leichtgewichtig ist wie alles andere von Swing auch. Das bedeutet: Das JFXPanel kann anders als AWT-Komponenten problemlos in Swing-Container wie die JScrollPane gesetzt werden, und auch die Transparenz funktioniert tadellos. Wenn also JFXPanel auf »nicht opak« gesetzt wird, scheint der Hintergrund durch. Das erlaubt eine perfekte Integration, die sich auch in der transparenten Ereignisbehandlung widerspiegelt. Ereignisse wie ein Klick auf eine JavaFX-Komponente kommen auch dort an und versanden nicht in Swing.
Ein wenig Mühe allerdings macht das Threading, denn Swing und JavaFX nutzen zwar beide Threads, doch unterschiedliche. Aus Swing ist bekannt, dass nach der Darstellung des Fensters Veränderungen an den Komponenten nur vom EDT (Event Dispatching Thread) erlaubt sind. Um Programmcode von einem Nicht-EDT- – wie dem Thread, der main() ausführt – im EDT-Thread zu platzieren, bietet die Swing-Bibliothek SwingUtilities.invokeLater(Runnable). JavaFX bietet die vergleichbare Methode Platform.runLater(Runnable), um Programmcode vom JavaFX-Thread ausführen zu lassen. Das ist in unserem Beispiel auch nötig, andernfalls wird fxPanel.setScene() aus dem main-Thread ausgeführt, was JavaFX mit einer IllegalStateException »Not on FX application thread; currentThread = main« bestraft. In JavaFX 8 sollten der JavaFX-Thread und der Event-Dispatch-Thread zusammenwachsen, doch ist das nicht standardmäßig aktiviert und muss erst über eine Property aktiviert werden.