You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JDK11环境下向JDialog添加JFXPanel后对话框无内容显示问题

问题背景
  • 程序基于Swing构建GUI,因需要使用CSS定制组件样式,采用JFXPanel替代常规JPanel实现收集用户输入的模态对话框
  • 运行环境为JDK 11
  • 异常表现:JFXPanel添加到JFrame时运行完全正常,但添加到JDialog时对话框无任何内容显示
  • 复现规律:从纯Swing组件的窗口触发打开该对话框时功能正常,从嵌入了JavaFX内容的JFXPanel所在窗口触发打开时故障复现

原实现代码

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Optional;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.embed.swing.JFXPanel;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;

public class JDialogExample {

    private Optional<String> output = Optional.empty();
    private final StackPane stackPane = new StackPane();
    private final JFXPanel panel = new JFXPanel();
    private final JDialog dialog = getNewJDialog();
    private final TextField tf = new TextField();

    public JDialogExample() {
        ObservableList list = stackPane.getChildren();
        list.addAll(new Label("My Label"), tf, getSubmitButton());
        panel.setScene(new Scene(stackPane));
        dialog.add(panel);
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setVisible(true);
    }

    public Optional<String> show() {
        return output;
    }

    private Button getSubmitButton() {
        Button btn = new Button();
        btn.setText("Submit");
        btn.setOnAction(new EventHandler<>() {
            @Override
            public void handle(javafx.event.ActionEvent t) {
                output = Optional.of(tf.getText());
                dialog.setVisible(false);
            }
        });
        return btn;
    }

    protected JDialog getNewJDialog() {
        return new JDialog(new JFrame(), "My dialog", true);
    }

    public static void main(String[] args) {
        Platform.startup(()
                -> {
                        JFrame frame1 = new JFrame();
                        JButton b = new JButton("This works");
                        b.addActionListener(new ActionListener() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                JDialogExample example = new JDialogExample();
                                Optional<String> userInputOptional = example.show();
                                if (userInputOptional.isPresent()) {
                                    System.out.println(userInputOptional.get());
                                }
                            }
                        });
                        frame1.add(b);
                        frame1.pack();
                        frame1.setVisible(true);
                        
                        JFrame frame2 = new JFrame();
                        StackPane stackPane2 = new StackPane();
                        JFXPanel jFXPanel = new JFXPanel();
                        Button b2 = new Button("This DOES NOT work");
                        b2.setOnAction(new EventHandler<>() {
                            @Override
                            public void handle(javafx.event.ActionEvent t) {
                                JDialogExample example = new JDialogExample();
                                Optional<String> userInputOptional = example.show();
                                if (userInputOptional.isPresent()) {
                                    System.out.println(userInputOptional.get());
                                }
                            }
                        });
                        ObservableList list2 = stackPane2.getChildren();
                        list2.addAll(b2);
                        jFXPanel.setScene(new Scene(stackPane2));
                        frame2.add(jFXPanel);
                        frame2.pack();
                        frame2.setLocationRelativeTo(null);
                        frame2.setVisible(true);
        });
    }

}
故障根因
  • 线程使用违规:Swing和JavaFX各自维护独立的UI事件线程,Swing组件的创建、修改、显示必须在Swing事件调度线程(EDT)执行,JavaFX组件、Scene的创建和修改必须在JavaFX Application Thread执行。原代码在JavaFX按钮的事件回调里直接创建、操作Swing的JDialog,跨线程操作UI会导致渲染初始化异常。
  • 模态窗口阻塞事件循环:模态JDialog显示时会阻塞父窗口的事件分发,当触发源是嵌了JFXPanel的窗口时,JavaFX渲染线程拿不到JFXPanel初始化所需的调度资源,直接导致面板内容无法绘制。
  • 尺寸计算错误:JFXPanel内容初始化存在延迟,pack()执行时如果JFXPanel还没完成首帧渲染,会拿到0x0的preferredSize,导致对话框内容区域被压缩到不可见。
修复方式
  • 严格遵守双UI线程规范:所有Swing操作通过SwingUtilities.invokeLater()投递到EDT执行,所有JavaFX场景操作通过Platform.runLater()投递到JavaFX线程执行,禁止跨线程直接操作UI组件。
  • 不要在JavaFX事件回调中直接弹出模态Swing对话框,回调中仅做线程投递,把对话框的创建、显示逻辑全部切到Swing EDT执行。
  • 给承载内容的JFXPanel设置合理的初始首选尺寸,避免pack()计算尺寸错误。

修复后核心逻辑示例

// JavaFX按钮点击回调中,切到Swing线程创建显示对话框
b2.setOnAction(t -> {
    SwingUtilities.invokeLater(() -> {
        JDialogExample example = new JDialogExample();
        Optional<String> res = example.show();
        res.ifPresent(System.out::println);
    });
});

// 构造方法中拆分线程操作
public JDialogExample() {
    dialog = getNewJDialog();
    panel = new JFXPanel();
    // 给JFXPanel设置初始尺寸
    panel.setPreferredSize(new java.awt.Dimension(300, 150));
    dialog.add(panel);
    // JavaFX内容初始化投递到FX线程
    Platform.runLater(() -> {
        stackPane = new StackPane();
        tf = new TextField();
        stackPane.getChildren().addAll(new Label("My Label"), tf, getSubmitButton());
        panel.setScene(new Scene(stackPane));
        // 场景设置完成后再在Swing线程调整对话框显示
        SwingUtilities.invokeLater(() -> {
            dialog.pack();
            dialog.setLocationRelativeTo(null);
            dialog.setVisible(true);
        });
    });
}

// 提交按钮操作切回Swing线程关闭对话框
btn.setOnAction(t -> {
    output = Optional.of(tf.getText());
    SwingUtilities.invokeLater(() -> dialog.setVisible(false));
});

// 主方法启动时,Swing窗口创建也要投递到EDT
public static void main(String[] args) {
    Platform.startup(() -> {
        SwingUtilities.invokeLater(() -> {
            // 放frame1、frame2的创建显示逻辑
        });
    });
}

修复后无论从纯Swing窗口还是嵌了JFXPanel的窗口触发,模态对话框都可以正常显示JavaFX内容,不会再出现空白问题。

内容的提问来源于stack exchange,提问作者BabaNew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 06:24:24