JFreeChart案例

wen java案例 2

JFreeChart实战案例全解析(附完整代码与优化指南)

目录导读

  1. JFreeChart为何仍是Java图表库的首选? —— 背景与核心优势
  2. 环境搭建与依赖引入 —— Maven/Gradle配置避坑指南
  3. 动态实时折线图(监控系统场景) —— 数据流刷新与性能优化
  4. 3D柱状图与交互式提示(销售报表场景) —— 自定义渲染与Tooltip
  5. 环形饼图与子图联动(问卷分析场景) —— 事件监听与布局合并
  6. 高频问答(FAQ) —— 中文乱码/中文水印/导出高清图/缩放卡顿
  7. SEO优化要点与总结 —— 如何让JFreeChart项目被搜索引擎青睐

JFreeChart为何仍是Java图表库的首选?

尽管ECharts、Chart.js等前端图表库占据大量流量,但在纯后端服务离线报表生成桌面应用(如Swing/JavaFX)以及对数据安全性要求极高的金融、政务系统中,JFreeChart凭借以下不可替代的特性,依然活跃在生产一线:

JFreeChart案例

  • 零依赖浏览器环境:直接输出PNG/JPEG/SVG/PDF,无需前端渲染。
  • 成熟稳定:自2000年发布至今,API设计稳如磐石,社区案例海量。
  • 高度可定制:从坐标轴刻度到渲染器,每个像素都可编程控制。
  • 与Spring Boot无缝整合:通过ChartFactory快速生成JFreeChart对象,再以流形式输出。

核心痛点:许多教程仅展示静态图,缺乏对实时数据流中文乱码高并发下内存溢出的实战处理,以下案例均从实际生产环境提取,已去伪存真。


环境搭建与依赖引入

Maven配置(避免版本冲突):

<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jfreechart</artifactId>
    <version>1.5.4</version> <!-- 最新稳定版,修复了多个渲染Bug -->
</dependency>
<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jcommon</artifactId>
    <version>1.0.24</version> <!-- 必须显式声明,否则可能拉取到旧版 -->
</dependency>

Gradle配置:

implementation 'org.jfree:jfreechart:1.5.4'
implementation 'org.jfree:jcommon:1.0.24'

避坑提示:若项目中同时存在POI或Apache XMLBeans,可能会冲突org.w3c.dom类,解决方案是在打包时使用shade插件并设置ServicesResourceTransformer


案例一:动态实时折线图(监控系统场景)

需求描述:每秒推送100个CPU/内存数据点,图表需平滑滚动,内存占用恒定。

核心代码实现(已优化):

public class RealtimeLineChart {
    private TimeSeries series;
    private JFreeChart chart;
    private ChartPanel panel;
    public RealtimeLineChart() {
        super("实时监控", true, true, false, false, true);
        series = new TimeSeries("CPU使用率", Millisecond.class);
        TimeSeriesCollection dataset = new TimeSeriesCollection(series);
        chart = ChartFactory.createTimeSeriesChart("服务器监控", "时间", "百分比", dataset);
        // 关键优化:关闭抗锯齿以提升刷新性能
        chart.setAntiAlias(false);
        XYPlot plot = chart.getXYPlot();
        plot.setDomainPannable(true);
        plot.setRangePannable(true);
    }
    // 向数据集中添加数据,并自动清理过期数据(保留最后500个点)
    public void addData(double value) {
        series.addOrUpdate(new Millisecond(), value);
        if (series.getItemCount() > 500) {
            series.delete(0, series.getItemCount() - 501);
        }
    }
    // 供外部定时器调用,使用SwingUtilities保证线程安全
    public void refreshUI() {
        SwingUtilities.invokeLater(() -> panel.repaint());
    }
}

性能优化要点

  • 使用TimeSeries而非XYSeries,自动处理时间戳排序。
  • 设置setAntiAlias(false),在实时场景下牺牲微小边缘平滑度换取3倍渲染速度。
  • 定期delete旧数据,防止内存溢出。

案例二:3D柱状图与交互式提示(销售报表场景)

需求描述:展示12个月×3个产品线的销售额,鼠标悬停需显示精确数值,并支持颜色高亮。

实现细节(含事件监听):

// 创建3D数据集(注意:3D效果需配合Renderer使用)
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
// ... 填充数据
JFreeChart chart = ChartFactory.createBarChart3D("年度销售报表", "月份", "销售额", dataset);
CategoryPlot plot = chart.getCategoryPlot();
BarRenderer3D renderer = (BarRenderer3D) plot.getRenderer();
// 自定义悬停提示
renderer.setDefaultToolTipGenerator((dataset, row, column) -> 
    "产品: " + dataset.getRowKey(row) + " 月: " + dataset.getColumnKey(column) 
    + " 销售额: " + dataset.getValue(row, column) + "万元");
// 高亮某个月(比如8月),设置特殊颜色
renderer.setSeriesPaint(1, new Color(255, 80, 80)); // 红色系列
// 设置柱子间距,避免3D视觉遮挡
renderer.setItemMargin(0.05);

易错点:3D图表默认使用CategoryAxis3D,若直接设置setCategoryLabelPositions可能导致标签重叠,建议统一使用CategoryLabelPositions.UP_45


案例三:环形饼图与子图联动(问卷分析场景)

需求描述:点击饼图某个扇区,中心显示对应选项的详细子图(如性别分布)。

联动实现方案(通过CombinedDataset):

// 父图:环形饼图
JFreeChart pieChart = ChartFactory.createRingChart("用户偏好", pieDataset, true, true, false);
// 子图:水平条形图(作为中心覆盖层)
JFreeChart subChart = ChartFactory.createBarChart("", "选项", "人数", subDataset, PlotOrientation.HORIZONTAL, false, true, false);
// 合并到同一个ChartPanel中,通过setPopupMenu添加触发事件
// 核心:为piePlot添加自定义的MarkerListener
RingPlot plot = (RingPlot) pieChart.getPlot();
plot.addChangeListener(event -> {
    if (event.getEntity() instanceof PieSectionEntity) {
        // 获取点击的扇区,动态替换子图数据集
        String key = (String) ((PieSectionEntity) event.getEntity()).getSectionKey();
        updateSubChart(key);
    }
});

视觉优化

  • 环形图默认中心空白,用setSectionOutlinesVisible(false)去掉描边,更显简洁。
  • 子图放置在 ChartPanelGlassPane 上,通过坐标计算居中。

高频问答(FAQ)

Q1:导出图片时中文全部变成方框(□□□)怎么办?

原因:JFreeChart默认使用Font.createFont加载系统字体,但Linux服务器(无中文字体)会导致崩溃或乱码。 解决方案

  1. 将Windows下的simsun.ttcmsyh.ttf放入项目resources/fonts
  2. 注册全局字体:
    Font font = Font.createFont(Font.TRUETYPE_FONT, 
     new FileInputStream("fonts/simsun.ttc")).deriveFont(14f);
    chart.getTitle().setFont(font);
    chart.getLegend().setItemFont(font);
    // 所有轴和Renderer逐一设置

Q2:如何导出高清无失真的SVG或PDF?

使用org.jfree.chart.encoders.SunPNGEncoderAdapter导出PNG,或者借助iTextJFreeChart直接写入PDF:

PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("out.pdf"));
document.open();
PdfContentByte cb = writer.getDirectContent();
PdfTemplate tp = cb.createTemplate(500, 300);
Graphics2D g2 = tp.createGraphics(500, 300, new PdfGraphics2D());
chart.draw(g2, new Rectangle2D.Double(0, 0, 500, 300));
g2.dispose();

Q3:数据量很大(10w+点)时缩放卡顿严重,如何优化?

  1. 改用XYSeries并设置setMaximumItemCount(10000)自动降采样。
  2. 重写XYItemRenderer,在drawItem方法中根据plot.getDomainAxis().getRange()判断是否在可视范围内,跳过不可见数据。
  3. 开启chart.setElementHinting(true)(1.5.0+版本)支持硬件加速。

Q4:JFreeChart是否支持触摸事件(触屏设备)?

原生ChartPanel不代表支持触摸,需继承ChartPanel重写processMouseMotionEvent,将鼠标坐标转换为MouseEvent的兄弟事件MouseWheelEvent(缩放),或利用JTouchChartPanel第三方库。


SEO优化要点与总结

让技术博客获得更多搜索流量的技巧:包含长尾关键词聚焦“JFreeChart案例+实战+代码”,命中“JFreeChart案例下载”、“JFreeChart折线图实例”等高频检索词,自然嵌入语义相近词:如“Java图表生成”、“Swing图表组件”、“后端报表方案”。

  • :使用H1-H3标签清晰分层,每个小节以问题型或痛点型开头,符合用户阅读习惯。
  • 代码段增加schema标记:使用<pre>配合json-ldcode类型,提升Google富摘要收录概率。
  • 内链建设:在文中链接到本博客其他相关教程(如“Spring Boot集成JFreeChart详解”),提升网站权重。

三个案例覆盖了JFreeChart最核心的三大场景:实时动态图3D交互报表联动分析,掌握了这些,你已经可以应对绝大多数企业级需求,值得注意的是,JFreeChart的API虽然臃肿,但正因如此,它才提供了无与伦比的控制粒度,在数据中台、老系统重构、以及批处理报表打印领域,它依然是不可替代的基石。

如果你需要在生产环境中解决更棘手的问题(如集群下图表缓存一致性、PostScript导出乱码),欢迎在评论区留言,我会逐一拆解,请务必注意:最新版1.5.4已修复了多个内存泄漏,请升级后再测试性能。

抱歉,评论功能暂时关闭!