首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JFreeChart 實戰:打造動態圖形報表 (折線圖/柱狀圖/環比) 完整代碼解析

     編輯:Java 相關知識

JFreeChart 是 Java 最成熟的開源圖表庫,支援折線、柱狀、餅圖、甘特、燭台等 20+ 圖型。本文以「情侶每日評分走勢圖」為趣味場景,示範從 Access 資料庫讀取資料、構建 TimeSeriesCollection/CategoryDataset、自訂標題/字體/背景/刻度、輸出高解析度 JPEG 的完整流程,附關鍵代碼與中文顯示調優技巧。

一、專案背景與資料模型

  • 資料表:curve_id, curve_owner(VARCHAR), curve_cent(DOUBLE), curve_year, curve_month, curve_day, note
  • 值物件 CurveVo:封裝年/月/日/分數/備註/所有人
  • 目標:月度折線趨勢圖 (雙序列對比)、季度/年度環比柱狀圖

二、核心繪圖類 (DrawPic)


package org.bromon.fiona.curve.util;

import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.*;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.*;
import java.awt.*;
import java.io.File;
import java.util.List;

public class DrawPic {

    // 1. 從 DB 讀取並構建 TimeSeriesCollection (折線圖專用)
    public TimeSeriesCollection getData(int year, int month) {
        List<CurveVo> dataOfFiona = dao.query(year, month, "fiona"); // 自行實作 JDBC
        List<CurveVo> dataOfBromon = dao.query(year, month, "bromon");

        TimeSeriesCollection dataset = new TimeSeriesCollection();
        TimeSeries fiona = new TimeSeries("Fiona");
        TimeSeries bromon = new TimeSeries("Bromon");

        for (CurveVo cv : dataOfFiona) {
            fiona.add(new TimeSeriesDataItem(new Day(cv.getDay(), cv.getMonth(), cv.getYear()), cv.getCent()));
        }
        for (CurveVo cv : dataOfBromon) {
            bromon.add(new TimeSeriesDataItem(new Day(cv.getDay(), cv.getMonth(), cv.getYear()), cv.getCent()));
        }
        dataset.addSeries(fiona);
        dataset.addSeries(bromon);
        return dataset;
    }

    // 2. 繪製折線圖並輸出 JPEG
    public String drawLineChart(int year, int month) {
        TimeSeriesCollection data = getData(year, month);
        JFreeChart chart = ChartFactory.createTimeSeriesChart(
            "Bromon & Fiona 愛情走勢圖",  // 主標題
            "時間",                       // X軸
            "分值",                       // Y軸
            data,                         // 資料集
            true, true, false             // 圖例、工具提示、URL
        );

        // 中文標題字體設定 (關鍵!預設字體無中文字型會顯示方塊)
        Font titleFont = new Font("微軟正黑體", Font.BOLD, 20);
        Font subFont = new Font("微軟正黑體", Font.PLAIN, 14);
        chart.setTitle(new TextTitle(chart.getTitle().getText(), titleFont));
        chart.addSubtitle(new TextTitle(year + "年" + month + "月", subFont));

        // 背景漸層
        chart.setBackgroundPaint(new GradientPaint(0, 0, Color.WHITE, 0, 1000, new Color(230, 240, 255)));

        // 繪圖區微調
        XYPlot plot = (XYPlot) chart.getPlot();
        plot.setBackgroundPaint(Color.WHITE);
        plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
        plot.setRangeGridlinePaint(Color.LIGHT_GRAY);

        // 輸出檔案
        String fileName = String.format("c:/pics/%d%02d_line.jpg", year, month);
        File dir = new File("c:/pics");
        if (!dir.exists()) dir.mkdirs();
        ChartUtilities.saveChartAsJPEG(new File(fileName), 1.0f, chart, 1000, 600); // 品質1.0, 寬1000, 高600
        return fileName;
    }

    // 3. 繪製環比柱狀圖 (CategoryDataset)
    public String drawBarChart(int year, int quarter) {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        // 假設 dao.queryQuarter 回傳 Map<String, Double> { "Fiona-Q1"=85.2, "Bromon-Q1"=78.5 ... }
        Map<String, Double> quarterData = dao.queryQuarter(year, quarter);
        quarterData.forEach((key, val) -> {
            String[] parts = key.split("-");
            dataset.addValue(val, parts[0], parts[1]); // (值, 序列, 類別)
        });

        JFreeChart chart = ChartFactory.createBarChart(
            year + "年 Q" + quarter + " 環比柱狀圖", "季度", "平均分", dataset,
            PlotOrientation.VERTICAL, true, true, false
        );
        // 同樣設定中文字體、背景...
        // Y軸強制整數刻度
        NumberAxis rangeAxis = (NumberAxis) chart.getCategoryPlot().getRangeAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

        String fileName = String.format("c:/pics/%d_Q%d_bar.jpg", year, quarter);
        ChartUtilities.saveChartAsJPEG(new File(fileName), 1.0f, chart, 800, 500);
        return fileName;
    }

    public static void main(String[] args) {
        new DrawPic().drawLineChart(2005, 3);
        new DrawPic().drawBarChart(2005, 1);
    }
}

三、中文顯示問題根治

原因:JFreeChart 預設邏輯字體 (SansSerif/Serif) 映射到的物理字體無中文字型。

方案:

  1. 代碼指定:如上範例 `new Font("微軟正黑體", ...)` / `"宋體"` / `"Microsoft YaHei"`
  2. 全域配置:建立 `ChartTheme` 繼承 `StandardChartTheme` 覆寫 `getRegularFont()`/`getLargeFont()`/`getSmallFont()`,再 `ChartFactory.setChartTheme(new MyTheme())`
  3. Docker/Linux 環境:安裝字體套件 (`apt-get install fonts-wqy-zenhei`),並確保 JVM 能找到字體目錄

四、常用圖表類型對應 Dataset 與 Factory 方法

圖型DatasetFactory Method
折線圖 (時間序列)TimeSeriesCollectioncreateTimeSeriesChart
柱狀圖 (類別)CategoryDataset / DefaultCategoryDatasetcreateBarChart / createStackedBarChart
餅圖PieDataset / DefaultPieDatasetcreatePieChart / createRingChart
散佈圖 / XY 圖XYDataset / XYSeriesCollectioncreateScatterPlot / createXYLineChart
甘特圖TaskSeriesCollectioncreateGanttChart
燭台圖 (股票)OHLCDatasetcreateCandlestickChart

五、效能與部署建議

  • 大量資料點 (>10k) 考慮抽樣或使用 XYSeries 而非 TimeSeries (後者含日期物件開銷大)
  • Servlet/Web 環境直接輸出 `OutputStream`:ChartUtilities.writeChartAsPNG(response.getOutputStream(), chart, w, h)
  • 依賴管理:Maven `org.jfreejfreechart1.5.3` (含 jcommon)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved