JavaFX应用添加SwingNode加载XChart热图时显示异常如何解决
问题描述
我有一个用于绘制热图的JavaFX应用,基于Oracle JDK 8在Windows 10设备上开发,使用xchart-3.8.0.jar库,通过SwingNode实现数据绘图。
相关代码
import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage; import javafx.scene.layout.VBox; import javafx.scene.layout.Priority; import javafx.embed.swing.SwingNode; import org.knowm.xchart.HeatMapChart; import org.knowm.xchart.HeatMapChartBuilder; import org.knowm.xchart.XChartPanel; import org.knowm.xchart.style.Styler; import javax.swing.*; import java.awt.*; import java.io.IOException; import java.util.Arrays; import java.util.Collections; public class XChartExample extends Application { @Override public void start(Stage stage) { VBox vBox = new VBox(); stage.setTitle("Heatmap"); HeatMapChart chart = new HeatMapChartBuilder().xAxisTitle("X").yAxisTitle("Y").theme(Styler.ChartTheme.Matlab).build(); chart.getStyler().setShowWithinAreaPoint(true); chart.getStyler().setLegendPosition(Styler.LegendPosition.OutsideS); chart.getStyler().setLegendLayout(Styler.LegendLayout.Horizontal); chart.getStyler().setPlotContentSize(1); chart.getStyler().setShowValue(true); int[] xData = new int[5]; xData[0] = 10; xData[1] = 20; xData[2] = 30; xData[3] = 40; xData[4] = 50; int[] yData = new int[5]; yData[0] = 10; yData[1] = 20; yData[2] = 30; yData[3] = 40; yData[4] = 50; int[][] heatData = new int[5][5]; for (int i = 0; i < 5; ++i) { for (int j = 0; j < 5; ++j) { heatData[i][j] = i + j; } } chart.addSeries("Basic HeatMap", xData, yData, heatData); SwingNode swingNode = new SwingNode(); createAndSetSwingContent(swingNode, chart); vBox.getChildren().add(swingNode); VBox.setVgrow(swingNode, Priority.ALWAYS); Scene scene = new Scene(vBox, 500, 350); stage.setTitle("Demo"); stage.setScene(scene); stage.show(); } private void createAndSetSwingContent(SwingNode swingNode, HeatMapChart chart) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JPanel chartPanel = new XChartPanel<>(chart); swingNode.setContent(chartPanel); } }); } public static void main(String[] args) { launch(args); } }
异常现象
- 运行应用时大概率出现黑窗口:

- 轻微拖动窗口后热图即可正常显示:

- 部分场景启动时可直接正常显示,但调整窗口大小或移动窗口后会再次出现黑屏,需要重新拖动窗口才能恢复显示
- 显存为4MB、未开启3D支持的Windows 10虚拟机中运行时不会出现该问题,应用可全程正常显示
解决方案
该问题的核心是Oracle JDK 8中JavaFX的SwingNode组件与硬件加速渲染逻辑存在兼容性缺陷,无3D加速的虚拟机环境下默认使用软件渲染,因此不会触发该异常。可通过以下方案解决:
- 方案1:强制JavaFX使用软件渲染。启动应用时添加JVM参数
-Dprism.order=sw即可,无需修改业务代码,适配成本最低。 - 方案2:代码层面主动触发重绘。在窗口显示、大小变化时主动触发Swing组件重绘,参考修改代码如下:
// 原stage.show()之后添加以下逻辑 stage.show(); // 初始化完成后主动重绘 SwingUtilities.invokeLater(() -> { if (swingNode.getContent() != null) { swingNode.getContent().repaint(); } }); // 监听窗口宽高变化触发重绘 stage.widthProperty().addListener((obs, oldVal, newVal) -> { SwingUtilities.invokeLater(() -> { if (swingNode.getContent() != null) { swingNode.getContent().repaint(); } }); }); stage.heightProperty().addListener((obs, oldVal, newVal) -> { SwingUtilities.invokeLater(() -> { if (swingNode.getContent() != null) { swingNode.getContent().repaint(); } }); });
- 方案3:升级JDK版本。升级到JDK 11及以上的LTS版本,高版本JDK已修复大量SwingNode与硬件渲染的兼容性问题,可从根源解决该异常。
- 方案4:替换绘图组件。改用纯JavaFX实现的热图绘制库,避免Swing与JavaFX跨UI框架混用,彻底规避这类兼容性问题。
内容的提问来源于stack exchange,提问作者asiokas
相关产品推荐
相关产品推荐

