JAXenter.de

Das Portal für Java, Architektur, Cloud & Agile
X

Alle Infos zur aktuellen JavaOne gibt's in unserer Serie: JavaOne 2014.

Swing Controls nach JavaFX umschreiben

JavaFX SwingNode: Migration leicht(er) gemacht

Oracle hat die Stimmen aus der Entwicklergemeinde erhört und stellt ab sofort mit SwingNode eine Node im JavaFX Scene Graph zur Verfügung, die die Migration erheblich erleichtern soll. Ein kurzer Überblick.

Nun, da JavaFX langsam auch für Non-Early-Adopter interessant wird, stellt sich die Frage der Migration von bestehenden Java-Swing-Anwendungen nach JavaFX. Bislang gab es als Antwort lediglich das JFXPanel. Dabei handelt es sich um die Möglichkeit, JavaFX in Java-Swing-Anwendungen zu integrieren. Das JFXPanel verhält sich dabei wie ein JPanel für Swing, kann jedoch eine JavaFX Scene darstellen. Diese Möglichkeit ist zwar interessant, aber leider nicht das, was sich die Entwickler gewünscht hätten. Üblicherweise bestehen die Swing-Anwendungen im Feld aus einer Vielzahl eigener, sehr komplexer Controls, welche man nur sehr ungern auf einmal in JavaFX umschreiben möchte. Für eine „sanfte“ Migration wünscht man sich eher den umgekehrten Weg, sprich: Man möchte gerne eine neue JavaFX-Anwendung aufsetzen und dann die mühsam erstellten Swing-Komponenten in JavaFX integrieren. Mit diesem Ansatz ist es dann möglich, langsam die bestehenden Swing Controls nach JavaFX umzuschreiben.

Glücklicherweise hat Oracle hier auf die Nachfrage der Entwicklergemeinde gehört und diesen Migrationspfad mit der so genannten SwingNode zur Verfügung gestellt. Diese stellt eine Node im Scene Graph dar, welche die Fähigkeit hat, Swing-Komponenten auf der Basis von JComponent zu rendern.

Bei beiden Migrationspfaden muss man aber immer bedenken, dass man es hier mit zwei unabhängigen Rendering-Threads zu tun hat, welche synchronisiert sein wollen. JavaFX kommt hier mit dem FXApplicationThread und Swing mit dem Event Dispatch Thread daher. Da beide Threads vollkommen unabhängig voneinander sind, muss der Entwickler dafür sorgen, dass Interaktionen zwischen beiden Threads über die entsprechenden Methoden in den jeweiligen Thread „eingefüttert“ werden. In JavaFX verwendet man hierzu die Methode:

Platform.runLater(new Runnable() { 
    @Override public void run() {
        // Your code here
    }
}); 
 

und in Java Swing:

SwingUtilities.invokeLater(new Runnable() {
    @Override public void run() {
        // Your code here 
    }
});

Als einfaches Beispiel soll nun in folgendem Listing ein Java Swing JButton in einer JavaFX SwingNode eingebettet werden und durch Drücken einen Text in einem JavaFX Label umschalten:

public class Migration extends Application {
    private Label   javaFxLabel;
    private Button  javaFxButton;
    private JLabel  swingLabel;
    private JButton swingButton;

    @Override public void init() {
        javaFxLabel  = new Label("No Button pressed");
        javaFxButton = new Button("click JavaFX");
        javaFxButton.setOnAction(new EventHandler<javafx.event.ActionEvent>() {
            @Override public void handle(javafx.event.ActionEvent actionEvent) {
                javaFxLabel.setText("JavaFX Button pressed");
                SwingUtilities.invokeLater(new Runnable() {
                    @Override public void run() {
                        swingLabel.setText("JavaFX Button pressed");
                    }
                });
            }
        });
    }

    @Override public void start(Stage stage) {
        final SwingNode swingNode = new SwingNode();

        // Call on Event Dispatch Thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override public void run() {
                swingLabel = new JLabel("no Button pressed");

                swingButton = new JButton("click Swing");
                swingButton.addActionListener(new ActionListener() {
                    @Override public void actionPerformed(ActionEvent e) {
                        swingLabel.setText("Swing Button pressed");

                        // Call on FX Application Thread
                        Platform.runLater(new Runnable() {
                            @Override public void run() {
                                javaFxLabel.setText("Swing Button pressed");
                            }
                        });
                    }
                });
                JPanel panel = new JPanel();
                panel.add(swingLabel);
                panel.add(swingButton);
                panel.setVisible(true);
                swingNode.setContent(panel);
            }
        });

        StackPane pane = new StackPane();
        pane.setPrefSize(300, 120);
        // Uncomment if NOT on OS X due to rendering problems with SwingNode
        //swingNode.setTranslateY(-40);

        HBox fxControls = new HBox();
        fxControls.setSpacing(10);
        fxControls.getChildren().addAll(javaFxLabel, javaFxButton);

        fxControls.setTranslateY(40);
        pane.getChildren().addAll(swingNode, fxControls);

        Scene scene = new Scene(pane, 300, 120);

        stage.setScene(scene);
        stage.show();
        stage.setTitle("Swing Migration");
    }

    public static void main (String[] args) {
        launch(args);
    }
}

Selbst wenn man sich dieses sehr einfache Beispiel anschaut, lässt sich bereits erahnen, dass es kein einfaches Unterfangen ist, eine bestehende Swing-Anwendung nach JavaFX zu portieren. Man muss dabei sehr genau darauf achten, dass man keine Threadingprobleme bekommt und sich immer im richtigen Thread befindet. Wer also nun erwartet hat, dass mit dem Einzug der SwingNode ins JDK8 (verfügbar im so genannten Developer Preview seit b89) alles viel einfacher wird, wird feststellen, dass sich zwar prinzipiell Swing-Komponenten in JavaFX miteinbinden lassen, man jedoch immer noch vor dem Problem der Thread-Synchronisierung steht, welches man nicht unterschätzen sollte.

Als Fazit kann man festhalten, dass von Oracle nun beide Möglichkeiten der Migration zur Verfügung gestellt werden (JavaFX in Swing mittels JFXPanel und Swing in JavaFX mittels SwingNode) und somit nun jeder selber wählen kann, was für ihn der beste Weg ist. Man sollte aber beachten, dass SwingNode momentan noch am Anfang steht, auf die erste stabile Version werden wir aber sicherlich nicht lange warten müssen.

 

Kommentare

Ihr Kommentar zum Thema

Als Gast kommentieren:

Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).