文档



JavaFX:互操作性
3 将JavaFX集成到Swing应用程序中(发布8)

3 将JavaFX集成到Swing应用程序中

本章介绍如何将JavaFX内容添加到Swing应用程序中,以及在单个应用程序中同时运行Swing和JavaFX内容时如何正确使用线程。

JavaFX SDK提供了位于javafx.embed.swing包中的JFXPanel类,它使您可以将JavaFX内容嵌入到Swing应用程序中。

将JavaFX内容添加到Swing组件中

为了本章的目的,您将创建一个JFrame组件,向其添加一个JFXPanel对象,并设置包含JavaFX内容的JFXPanel组件的图形场景。

与任何Swing应用程序一样,您需要在事件分派线程(EDT)上创建图形用户界面(GUI)。示例3-1显示了initAndShowGUI方法,该方法创建一个JFrame组件并向其添加一个JFXPanel对象。创建JFXPanel类的实例隐式启动了JavaFX运行时。创建GUI后,调用initFX方法在JavaFX应用程序线程上创建JavaFX场景。

示例3-1

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Test {

    private static void initAndShowGUI() {
        // 此方法在EDT线程上调用
        JFrame frame = new JFrame("Swing和JavaFX");
        final JFXPanel fxPanel = new JFXPanel();
        frame.add(fxPanel);
        frame.setSize(300, 200);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                initFX(fxPanel);
            }
       });
    }

    private static void initFX(JFXPanel fxPanel) {
        // 此方法在JavaFX线程上调用
        Scene scene = createScene();
        fxPanel.setScene(scene);
    }

    private static Scene createScene() {
        Group  root  =  new  Group();
        Scene  scene  =  new  Scene(root, Color.ALICEBLUE);
        Text  text  =  new  Text();
        
        text.setX(40);
        text.setY(100);
        text.setFont(new Font(25));
        text.setText("欢迎使用JavaFX!");

        root.getChildren().add(text);

        return (scene);
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                initAndShowGUI();
            }
        });
    }
}
3 将JavaFX集成到Swing应用程序中(8版)

Swing-JavaFX互操作性和线程

在单个应用程序中同时存在JavaFX和Swing数据时,可能会遇到以下互操作性情况:

  • 通过更改Swing数据触发JavaFX数据的更改。

  • 通过更改JavaFX数据触发Swing数据的更改。

响应Swing数据更改而更改JavaFX数据

JavaFX数据应仅在JavaFX用户线程上访问。每当您必须更改JavaFX数据时,将您的代码封装到一个Runnable对象中,并调用Platform.runLater方法,如示例3-2所示。

示例3-2

jbutton.addActionListener(new ActionListener() { 
    public void actionPerformed(ActionEvent e) { 
        Platform.runLater(new Runnable() { 
            @Override
            public void run() {
                fxlabel.setText("Swing按钮被点击!"); 
            }
        });
    }
});

响应JavaFX数据更改而更改Swing数据

Swing数据应仅在EDT上更改。为确保您的代码在EDT上执行,将其封装到一个Runnable对象中,并调用SwingUtilities.invokeLater方法,如示例3-3所示。

示例3-3

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        //更改Swing数据的代码。
    }
});
3 将JavaFX集成到Swing应用程序中(8版)

介绍SimpleSwingBrowser应用程序

为了了解Swing-JavaFX互操作性的工作原理,可以考虑使用SimpleSwingBrowser应用程序。这是一个带有集成JavaFX组件的Swing应用程序,用于查看网页。您可以在地址栏中输入URL并在应用程序窗口中查看加载的页面。 SimpleSwingBrowser应用程序窗口如图3-1所示。

图3-1 SimpleSwingBrowser应用程序窗口

图3-1的描述如下
"图3-1 SimpleSwingBrowser应用程序窗口"的描述

3 将JavaFX集成到Swing应用程序中(发布8)

初始化Swing数据

您可以查看SimpleSwingBrowser.java文件,或者下载包含NetBeans项目的SimpleSwingBrowser.zip文件。从zip文件中提取文件到本地文件系统上的一个目录,并在Netbeans IDE中运行该项目。

从版本7.2开始,NetBeans IDE支持嵌入JavaFX内容的Swing应用程序。创建新项目时,在JavaFX类别中选择Swing应用程序中的JavaFX

注意:

要在防火墙后运行此应用程序,必须指定代理设置,以便应用程序可以访问远程资源。

在NetBeans IDE中,右键单击项目窗口中的SimpleSwingBrowser项目,选择属性,在项目属性对话框中选择运行

在VM选项字段中,以以下格式设置代理:

-Dhttp.proxyHost=webcache.mydomain.com -Dhttp.proxyPort=8080

SimpleSwingBrowser应用程序的GUI在应用程序启动时在EDT上创建。 main方法的实现如示例3-4所示。

示例3-4

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            SimpleSwingBrowser browser = new SimpleSwingBrowser();
            browser.setVisible(true);
            browser.loadURL("http://oracle.com");
        }
    });
}

SimpleSwingBrowser类初始化Swing对象,并调用initComponents方法创建GUI,如示例3-5所示。

示例 3-5

public class SimpleSwingBrowser extends JFrame {

    private final JFXPanel jfxPanel = new JFXPanel();
    private WebEngine engine;
    
    private final JPanel panel = new JPanel(new BorderLayout());
    private final JLabel lblStatus = new JLabel();

    private final JButton btnGo = new JButton("前往");
    private final JTextField txtURL = new JTextField();
    private final JProgressBar progressBar = new JProgressBar(); 

    public SimpleSwingBrowser() {
        super();
        initComponents();
    }

    private void initComponents() {
        createScene();
    
        ActionListener al = new ActionListener() {
            @Override 
            public void actionPerformed(ActionEvent e) {
                loadURL(txtURL.getText());
            }
        };

        btnGo.addActionListener(al);
        txtURL.addActionListener(al);

        progressBar.setPreferredSize(new Dimension(150, 18));
        progressBar.setStringPainted(true);

        JPanel topBar = new JPanel(new BorderLayout(5, 0));
        topBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
        topBar.add(txtURL, BorderLayout.CENTER);
        topBar.add(btnGo, BorderLayout.EAST);

        JPanel statusBar = new JPanel(new BorderLayout(5, 0));
        statusBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
        statusBar.add(lblStatus, BorderLayout.CENTER);
        statusBar.add(progressBar, BorderLayout.EAST);

        panel.add(topBar, BorderLayout.NORTH);
        panel.add(jfxPanel, BorderLayout.CENTER);
        panel.add(statusBar, BorderLayout.SOUTH);

        getContentPane().add(panel);

        setPreferredSize(new Dimension(1024, 600));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
    }
}

该应用程序的顶层窗口是一个 JFrame 对象,其中包含各种 Swing 组件,如文本字段、按钮、进度条和用于显示 JavaFX 内容的 JFX 面板。

3 将JavaFX集成到Swing应用程序中(发布8)

加载JavaFX内容

在第一次运行时,将http://oracle.com网页加载到WebView对象中。当在地址栏中输入新的URL时,动作监听器(附加在initComponents方法中的txtURL文本字段上)会启动页面加载,如示例3-6所示。

示例3-6

ActionListener al = new ActionListener() {
    @Override public void actionPerformed(ActionEvent e) {
        loadURL(txtURL.getText());
    }
};

JavaFX数据只能在JavaFX应用程序线程上访问。loadURL方法将代码封装到一个Runnable对象中,并调用Platform.runLater方法,如示例3-7所示。

示例3-7

public void loadURL(final String url) {
    Platform.runLater(new Runnable() {
        @Override public void run() {
            String tmp = toURL(url);

            if (url == null) {
            tmp = toURL("http://" + url);
            }

           engine.load(tmp);
        }
    });
}

private static String toURL(String str) {
    try {
        return new URL(str).toExternalForm();
    } catch (MalformedURLException exception) {
        return null;
    }
}

更新Swing数据

当将新页面加载到WebView组件中时,从JavaFX数据中检索页面的标题,并将其传递给Swing GUI,以作为标题放置在应用程序窗口上。这个行为在createScene方法中实现,如示例3-8所示。

示例3-8

private void createScene() {

    Platform.runLater(new Runnable() {
        @Override
        public void run() {

            WebView view = new WebView();
            engine = view.getEngine();

            engine.titleProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue<? extends String> observable, 
String oldValue, final String newValue) {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override 
                        public void run() {
                            SimpleSwingBrowser.this.setTitle(newValue);
                        }
                    });
                }
            });
        }
    });
}

应用程序文件

源代码 

NetBeans项目 

关闭窗口

目录

JavaFX: 互操作性

展开 折叠