JFreeChart 是 Java 最成熟的開源圖表庫,支援折線、柱狀、餅圖、甘特、燭台等 20+ 圖型。本文以「情侶每日評分走勢圖」為趣味場景,示範從 Access 資料庫讀取資料、構建 TimeSeriesCollection/CategoryDataset、自訂標題/字體/背景/刻度、輸出高解析度 JPEG 的完整流程,附關鍵代碼與中文顯示調優技巧。
curve_id, curve_owner(VARCHAR), curve_cent(DOUBLE), curve_year, curve_month, curve_day, noteCurveVo:封裝年/月/日/分數/備註/所有人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) 映射到的物理字體無中文字型。
方案:
| 圖型 | Dataset | Factory Method |
|---|---|---|
| 折線圖 (時間序列) | TimeSeriesCollection | createTimeSeriesChart |
| 柱狀圖 (類別) | CategoryDataset / DefaultCategoryDataset | createBarChart / createStackedBarChart |
| 餅圖 | PieDataset / DefaultPieDataset | createPieChart / createRingChart |
| 散佈圖 / XY 圖 | XYDataset / XYSeriesCollection | createScatterPlot / createXYLineChart |
| 甘特圖 | TaskSeriesCollection | createGanttChart |
| 燭台圖 (股票) | OHLCDataset | createCandlestickChart |
XYSeries 而非 TimeSeries (後者含日期物件開銷大)ChartUtilities.writeChartAsPNG(response.getOutputStream(), chart, w, h)jcommon)