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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 14:12:02