ZetCode

JFreeChart

最后修改于 2024 年 1 月 27 日

在本文中,我们将学习如何使用 JFreeChart。我们将展示如何创建各种类型的图表。图表显示在 Swing 应用程序中,并保存到图像文件中。我们使用 Java servlet 在 Web 浏览器中创建和呈现图表,并从 MySQL 数据库检索图表数据。

JFreeChart 库

图表是一种以简单方式显示信息的图形,通常使用线条和曲线来显示数量。JFreeChart 是一个流行的 Java 图表创建库。 JFreeChart 允许创建各种交互式和非交互式图表。我们可以创建折线图、柱状图、面积图、散点图、饼图、甘特图以及各种专用图表,例如风向图或气泡图。

JFreeChart 具有广泛的可定制性;它允许修改图表项目、图例、线条或标记的颜色和样式。它可以自动绘制坐标轴刻度和图例。图表具有内置的鼠标缩放功能。现有的图表可以通过库在数据集合上拥有的监听器轻松更新。它支持多种输出格式,包括 PNG、JPEG、PDF 和 SVG。

JFreeChart 由 David Gilbert 于 2000 年启动。如今,JFreeChart 是 Java 开发人员中使用最广泛的图表库。

JFreeChart Maven 依赖

<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jfreechart</artifactId>
    <version>1.5.3</version>
</dependency>

对于我们的项目,我们使用这个 Maven 依赖。

JFreeChart 直方图

直方图是数值数据分布的准确表示。它是连续变量的概率分布的估计。

com/zetcode/HistogramEx.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.data.statistics.HistogramDataset;

import java.io.File;
import java.io.IOException;

public class HistogramEx {

    public static void main(String[] args) throws IOException {

        double[] vals = {

                0.71477137, 0.55749811, 0.50809619, 0.47027228, 0.25281568,
                0.66633175, 0.50676332, 0.6007552, 0.56892904, 0.49553407,
                0.61093935, 0.65057417, 0.40095626, 0.45969447, 0.51087888,
                0.52894806, 0.49397198, 0.4267163, 0.54091298, 0.34545257,
                0.58548892, 0.3137885, 0.63521146, 0.57541744, 0.59862265,
                0.66261386, 0.56744017, 0.42548488, 0.40841345, 0.47393027,
                0.60882106, 0.45961208, 0.43371424, 0.40876484, 0.64367337,
                0.54092033, 0.34240811, 0.44048106, 0.48874236, 0.68300902,
                0.33563968, 0.58328107, 0.58054283, 0.64710522, 0.37801285,
                0.36748982, 0.44386445, 0.47245989, 0.297599, 0.50295541,
                0.39785732, 0.51370486, 0.46650358, 0.5623638, 0.4446957,
                0.52949791, 0.54611411, 0.41020067, 0.61644868, 0.47493691,
                0.50611458, 0.42518211, 0.45467712, 0.52438467, 0.724529,
                0.59749142, 0.45940223, 0.53099928, 0.65159718, 0.38038268,
                0.51639554, 0.41847437, 0.46022878, 0.57326103, 0.44913632,
                0.61043611, 0.42694949, 0.43997814, 0.58787928, 0.36252603,
                0.50937634, 0.47444256, 0.57992527, 0.29381335, 0.50357977,
                0.42469464, 0.53049697, 0.7163579, 0.39741694, 0.41980533,
                0.68091159, 0.69330702, 0.50518926, 0.55884098, 0.48618324,
                0.48469854, 0.55342267, 0.67159111, 0.62352006, 0.34773486};


        var dataset = new HistogramDataset();
        dataset.addSeries("key", vals, 50);

        JFreeChart histogram = ChartFactory.createHistogram("Normal distribution",
                "y values", "x values", dataset);

        ChartUtils.saveChartAsPNG(new File("histogram.png"), histogram, 450, 400);
    }
}

在此示例中,我们为值的随机分布生成直方图。该图表生成为 PNG 文件并保存在磁盘上。

var dataset = new HistogramDataset();
dataset.addSeries("key", vals, 50);

直方图在 HistogramDataset 中显示值。 addSeries 方法的第二个参数是双精度值数组。第三个是 bin 的数量,即图表上绘制的矩形的数量。

JFreeChart histogram = ChartFactory.createHistogram("Normal distribution",
    "y values", "x values", dataset);

直方图使用 ChartFactory.createHistogram 方法创建。

ChartUtils.saveChartAsPNG(new File("histogram.png"), histogram, 450, 400);

我们使用 ChartUtils.saveChartAsPNG 将图表写入 PNG 图像。

JFreeChart 折线图

折线图是一种基本的图表类型,它将信息显示为一系列由直线段连接的数据点。 JavaFX 中的折线图使用 ChartFactory.createXYLineChart 创建。

com/zetcode/LineChartEx.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;

public class LineChartEx extends JFrame {

    public LineChartEx() {

        initUI();
    }

    private void initUI() {

        XYDataset dataset = createDataset();
        JFreeChart chart = createChart(dataset);

        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        chartPanel.setBackground(Color.white);
        add(chartPanel);

        pack();
        setTitle("Line chart");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private XYDataset createDataset() {

        var series = new XYSeries("2016");
        series.add(18, 567);
        series.add(20, 612);
        series.add(25, 800);
        series.add(30, 980);
        series.add(40, 1410);
        series.add(50, 2350);

        var dataset = new XYSeriesCollection();
        dataset.addSeries(series);

        return dataset;
    }

    private JFreeChart createChart(XYDataset dataset) {

        JFreeChart chart = ChartFactory.createXYLineChart(
                "Average salary per age",
                "Age",
                "Salary (€)",
                dataset,
                PlotOrientation.VERTICAL,
                true,
                true,
                false
        );

        XYPlot plot = chart.getXYPlot();

        var renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesPaint(0, Color.RED);
        renderer.setSeriesStroke(0, new BasicStroke(2.0f));

        plot.setRenderer(renderer);
        plot.setBackgroundPaint(Color.white);

        plot.setRangeGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.BLACK);

        plot.setDomainGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.BLACK);

        chart.getLegend().setFrame(BlockBorder.NONE);

        chart.setTitle(new TextTitle("Average Salary per Age",
                        new Font("Serif", java.awt.Font.BOLD, 18)
                )
        );

        return chart;
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new LineChartEx();
            ex.setVisible(true);
        });
    }
}

在此示例中,我们创建一个折线图,显示每个年龄段的平均工资。

var series = new XYSeries("2016");
series.add(18, 567);
series.add(20, 612);
series.add(25, 800);
...

XYSeries 表示 (x, y) 形式的零个或多个数据项序列。

var dataset = new XYSeriesCollection();
dataset.addSeries(series);

该序列被添加到 XYSeriesCollection,它是可用作数据集的 XYSeries 对象的集合。

JFreeChart chart = ChartFactory.createXYLineChart(
        "Average salary per age", 
        "Age", 
        "Salary (€)", 
        dataset, 
        PlotOrientation.VERTICAL,
        true, 
        true, 
        false 
);

ChartFactory.createXYLineChart 创建一个新的折线图。该方法的参数为:图表标题、X 轴标签、Y 轴标签、数据、绘图方向以及三个标志,指示是否显示图例、工具提示和 URL。

XYPlot plot = chart.getXYPlot();

我们获取对绘图的引用以便对其进行自定义。

var renderer = new XYLineAndShapeRenderer();
renderer.setSeriesPaint(0, Color.RED);
renderer.setSeriesStroke(0, new BasicStroke(2.0f));
plot.setRenderer(renderer);

在这里,我们为图表的线条设置笔触和颜色。 XYLineAndShapeRenderer 是一个用线条连接数据点和/或在每个数据点绘制形状的对象。渲染器使用 setRenderer 方法设置。

plot.setBackgroundPaint(Color.white);

setBackgroundPaint 设置绘图区域的背景颜色。

plot.setRangeGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.BLACK);

plot.setDomainGridlinesVisible(true);
plot.setDomainGridlinePaint(Color.BLACK);

我们显示网格线并以黑色绘制。

chart.getLegend().setFrame(BlockBorder.NONE);

我们删除图例周围的边框。

chart.setTitle(new TextTitle("Average Salary per Age",
                new Font("Serif", java.awt.Font.BOLD, 18)
        )
);

我们使用新字体创建图表标题。

Line chart with JFreeChart
图:折线图

具有两个序列的折线图

在第二个示例中,我们创建一个包含两个数据序列的折线图。

com/zetcode/LineChartEx2.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;

public class LineChartEx2 extends JFrame {

    public LineChartEx2() {

        initUI();
    }

    private void initUI() {

        XYDataset dataset = createDataset();
        JFreeChart chart = createChart(dataset);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        chartPanel.setBackground(Color.white);
        
        add(chartPanel);

        pack();
        setTitle("Line chart");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private XYDataset createDataset() {

        var series1 = new XYSeries("2014");
        series1.add(18, 530);
        series1.add(20, 580);
        series1.add(25, 740);
        series1.add(30, 901);
        series1.add(40, 1300);
        series1.add(50, 2219);

        var series2 = new XYSeries("2016");
        series2.add(18, 567);
        series2.add(20, 612);
        series2.add(25, 800);
        series2.add(30, 980);
        series2.add(40, 1210);
        series2.add(50, 2350);

        var dataset = new XYSeriesCollection();
        dataset.addSeries(series1);
        dataset.addSeries(series2);

        return dataset;
    }

    private JFreeChart createChart(final XYDataset dataset) {

        JFreeChart chart = ChartFactory.createXYLineChart(
                "Average salary per age",
                "Age",
                "Salary (€)",
                dataset,
                PlotOrientation.VERTICAL,
                true,
                true,
                false
        );

        XYPlot plot = chart.getXYPlot();

        var renderer = new XYLineAndShapeRenderer();

        renderer.setSeriesPaint(0, Color.RED);
        renderer.setSeriesStroke(0, new BasicStroke(2.0f));
        renderer.setSeriesPaint(1, Color.BLUE);
        renderer.setSeriesStroke(1, new BasicStroke(2.0f));

        plot.setRenderer(renderer);
        plot.setBackgroundPaint(Color.white);
        plot.setRangeGridlinesVisible(false);
        plot.setDomainGridlinesVisible(false);

        chart.getLegend().setFrame(BlockBorder.NONE);

        chart.setTitle(new TextTitle("Average Salary per Age",
                        new Font("Serif", Font.BOLD, 18)
                )
        );

        return chart;
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new LineChartEx2();
            ex.setVisible(true);
        });
    }
}

该示例绘制一个包含两个数据序列的折线图。

var series1 = new XYSeries("2014");
series1.add(18, 530);
series1.add(20, 580);
series1.add(25, 740);
...

我们创建第一个系列;它包含 2014 年的数据。

var series2 = new XYSeries("2016");
series2.add(18, 567);
series2.add(20, 612);
series2.add(25, 800);
...

创建第二个数据系列;它包含 2016 年的数据。

var dataset = new XYSeriesCollection();
dataset.addSeries(series1);
dataset.addSeries(series2);

这些序列使用 addSeries 方法添加到 XYSeriesCollection

renderer.setSeriesPaint(0, Color.RED);
renderer.setSeriesStroke(0, new BasicStroke(2.0f));

renderer.setSeriesPaint(1, Color.BLUE);
renderer.setSeriesStroke(1, new BasicStroke(2.0f)); 

一条线以红色绘制,另一条线以蓝色绘制。

plot.setRangeGridlinesVisible(false);
plot.setDomainGridlinesVisible(false);

网格线已关闭。

Line chart with two series
图:具有两个序列的折线图

将折线图保存到图像

ChartUtils 是 JFreeChart 的实用程序方法集合。它包括将图表转换为图像格式和创建简单 HTML 图像映射的方法。

com/zetcode/LineChartToPNGEx.java
package com.zetcode;

import java.io.File;
import java.io.IOException;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class LineChartToPNGEx {

    public static void main(String[] args) throws IOException {

        var series1 = new XYSeries("2014");
        series1.add(18, 530);
        series1.add(20, 580);
        series1.add(25, 740);
        series1.add(30, 901);
        series1.add(40, 1300);
        series1.add(50, 2219);

        var dataset = new XYSeriesCollection();
        dataset.addSeries(series1);

        JFreeChart chart = ChartFactory.createXYLineChart(
                "Average salary per age",
                "Age",
                "Salary (€)",
                dataset,
                PlotOrientation.VERTICAL,
                true,
                true,
                false
        );

        ChartUtils.saveChartAsPNG(new File("line_chart.png"), chart, 450, 400);
    }
}

该示例创建一个折线图并将其保存到 PNG 文件中。

ChartUtils.saveChartAsPNG(new File("line_chart.png"), chart, 450, 400);

ChartUtils.saveChartAsPNG 将图表以 PNG 格式保存到指定的文件中。

JFreeChart 气泡图

气泡图是具有附加维度的散点图的变体。我们使用气泡而不是数据点。前两个维度可视化为坐标。气泡的大小表示附加维度。

气泡图在 JFreeChart 中使用 ChartFactory.createBubbleChart 方法创建。

com/zetcode/BubbleChartEx.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBubbleRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.DefaultXYZDataset;
import org.jfree.data.xy.XYZDataset;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;


public class BubbleChartEx extends JFrame {

    public BubbleChartEx() {

        initUI();
    }

    private void initUI() {

        XYZDataset dataset = createDataset();

        JFreeChart chart = createChart(dataset);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        chartPanel.setBackground(Color.white);
        add(chartPanel);

        pack();

        setTitle("Bubble chart");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private XYZDataset createDataset() {

        var dataset = new DefaultXYZDataset();

        dataset.addSeries("Product A", new double[][]{{50}, {642}, {23 * 5}});
        dataset.addSeries("Product B", new double[][]{{23}, {540}, {47 * 5}});
        dataset.addSeries("Product C", new double[][]{{19}, {188}, {30 * 5}});

        return dataset;
    }

    private JFreeChart createChart(XYZDataset dataset) {

        JFreeChart chart = ChartFactory.createBubbleChart(
                "Products",
                "Quantity",
                "Price",
                dataset,
                PlotOrientation.VERTICAL,
                true,
                true,
                false);

        XYPlot plot = (XYPlot) chart.getPlot();

        NumberAxis nax1 = (NumberAxis) plot.getDomainAxis();
        nax1.setLowerMargin(0.2);
        nax1.setUpperMargin(0.2);

        NumberAxis nax2 = (NumberAxis) plot.getRangeAxis();
        nax2.setLowerMargin(0.9);
        nax2.setUpperMargin(0.9);

        XYBubbleRenderer renderer = (XYBubbleRenderer) plot.getRenderer();
        renderer.setDefaultItemLabelsVisible(true);

        chart.setTitle(new TextTitle("Products",
                new Font("Serif", java.awt.Font.BOLD, 18))
        );

        return chart;
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new BubbleChartEx();
            ex.setVisible(true);
        });
    }
}

我们有三种产品。对于这些产品,我们有它们的数量、价格和市场份额。数量放在 x 轴上,价格放在 y 轴上。市场份额值是 z 值;它们由气泡表示。

private XYZDataset createDataset() {

    var dataset = new DefaultXYZDataset();

    dataset.addSeries("Product A", new double[][]{{50}, {642}, {23 * 5}});
    dataset.addSeries("Product B", new double[][]{{23}, {540}, {47 * 5}});
    dataset.addSeries("Product C", new double[][]{{19}, {188}, {30 * 5}});

    return dataset;
}

气泡图使用 XYZDataset。 z 值(市场份额)已调整;我们使圆圈更大。

JFreeChart chart = ChartFactory.createBubbleChart(
    "Products",
    "Quantity",
    "Price",
    dataset,
    PlotOrientation.VERTICAL,
    true,
    true,
    false);

我们使用 ChartFactory.createBubbleChart 创建图表。

NumberAxis nax1 = (NumberAxis) plot.getDomainAxis();
nax1.setLowerMargin(0.2);
nax1.setUpperMargin(0.2);

NumberAxis nax2 = (NumberAxis) plot.getRangeAxis();
nax2.setLowerMargin(0.9);
    nax2.setUpperMargin(0.9);

我们调整轴上的值,以便所有气泡都可见。

JFreeChart 面积图

面积图以图形方式显示随时间变化的定量数据。面积图在 JFreeChart 中使用 ChartFactory.createAreaChart 方法创建。

com/zetcode/AreaChartEx.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.AreaRendererEndType;
import org.jfree.chart.renderer.category.AreaRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtils;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;

public class AreaChartEx extends JFrame {

    public AreaChartEx() {

        initUI();
    }

    private void initUI() {

        CategoryDataset dataset = createDataset();

        JFreeChart chart = createChart(dataset);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        chartPanel.setBackground(Color.white);
        add(chartPanel);

        pack();
        setTitle("Area chart");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private CategoryDataset createDataset() {

        double[][] data = new double[][]{
                {82502, 84026, 85007, 86216, 85559, 84491, 87672,
                        88575, 89837, 90701}
        };

        CategoryDataset dataset = DatasetUtils.createCategoryDataset(
                new String[]{"Oil"}, new String[]{"2004", "2005", "2006",
                        "2007", "2008", "2009", "2010", "2011", "2012", "2013"},
                data
        );

        return dataset;
    }

    private JFreeChart createChart(CategoryDataset dataset) {

        JFreeChart chart = ChartFactory.createAreaChart(
                "Oil consumption",
                "Time",
                "Thousands bbl/day",
                dataset,
                PlotOrientation.VERTICAL,
                false,
                true,
                true
        );

        CategoryPlot plot = (CategoryPlot) chart.getPlot();
        plot.setForegroundAlpha(0.3f);

        AreaRenderer renderer = (AreaRenderer) plot.getRenderer();
        renderer.setEndType(AreaRendererEndType.LEVEL);

        chart.setTitle(new TextTitle("Oil consumption",
                new Font("Serif", java.awt.Font.BOLD, 18))
        );

        return chart;
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new AreaChartEx();
            ex.setVisible(true);
        });
    }
}

该示例显示了一个面积图,显示了世界原油按年份的消费量。

double[][] data = new double[][]{
    {82502, 84026, 85007, 86216, 85559, 84491, 87672,
        88575, 89837, 90701}
};

CategoryDataset dataset = DatasetUtils.createCategoryDataset(
    new String[]{"Oil"}, new String[]{"2004", "2005", "2006",
            "2007", "2008", "2009", "2010", "2011", "2012", "2013"},
    data
);

数据集是使用 DatasetUtils.createCategoryDataset 方法创建的。类别数据集的值与类别关联。在我们的示例中,我们有与石油消费相关的年份。

CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.setForegroundAlpha(0.3f);

我们使用 setForegroundAlpha 方法使图表透明。

AreaRenderer renderer = (AreaRenderer) plot.getRenderer();
renderer.setEndType(AreaRendererEndType.LEVEL);

我们调整图表的结尾。

Area chart
图:面积图

JFreeChart 柱状图

条形图使用矩形条呈现分组数据,条的长度与它们表示的值成比例。 这些条可以垂直或水平绘制。

com/zetcode/BarChartEx.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import java.awt.Color;
import java.awt.EventQueue;

public class BarChartEx extends JFrame {

    public BarChartEx() {

        initUI();
    }

    private void initUI() {

        CategoryDataset dataset = createDataset();

        JFreeChart chart = createChart(dataset);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        chartPanel.setBackground(Color.white);
        add(chartPanel);

        pack();
        setTitle("Bar chart");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private CategoryDataset createDataset() {

        var dataset = new DefaultCategoryDataset();
        dataset.setValue(46, "Gold medals", "USA");
        dataset.setValue(38, "Gold medals", "China");
        dataset.setValue(29, "Gold medals", "UK");
        dataset.setValue(22, "Gold medals", "Russia");
        dataset.setValue(13, "Gold medals", "South Korea");
        dataset.setValue(11, "Gold medals", "Germany");

        return dataset;
    }

    private JFreeChart createChart(CategoryDataset dataset) {

        JFreeChart barChart = ChartFactory.createBarChart(
                "Olympic gold medals in London",
                "",
                "Gold medals",
                dataset,
                PlotOrientation.VERTICAL,
                false, true, false);

        return barChart;
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new BarChartEx();
            ex.setVisible(true);
        });
    }
}

代码示例使用柱状图显示伦敦 2012 年每个国家/地区的奥运金牌数量。

var dataset = new DefaultCategoryDataset();
dataset.setValue(46, "Gold medals", "USA");
dataset.setValue(38, "Gold medals", "China");
dataset.setValue(29, "Gold medals", "UK");
dataset.setValue(22, "Gold medals", "Russia");
dataset.setValue(13, "Gold medals", "South Korea");
dataset.setValue(11, "Gold medals", "Germany");

我们使用 DefaultCategoryDataset 创建数据集。

JFreeChart barChart = ChartFactory.createBarChart(
        "Olympic gold medals in London",
        "",
        "Gold medals",
        dataset,
        PlotOrientation.VERTICAL,
        false, true, false);

柱状图使用 ChartFactory.createBarChart 方法创建。

Bar chart
图:柱状图

JFreeChart 饼图

饼图是一个圆形图表,分为多个切片以说明数值比例。饼图在 JFreeChart 中使用 ChartFactory.createPieChart 方法创建。

com/zetcode/PieChartEx.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.data.general.DefaultPieDataset;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import java.awt.Color;
import java.awt.EventQueue;

public class PieChartEx extends JFrame {

    public PieChartEx() {

        initUI();
    }

    private void initUI() {

        DefaultPieDataset dataset = createDataset();

        JFreeChart chart = createChart(dataset);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        chartPanel.setBackground(Color.white);
        add(chartPanel);

        pack();
        setTitle("Pie chart");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private DefaultPieDataset createDataset() {

        var dataset = new DefaultPieDataset();
        dataset.setValue("Apache", 52);
        dataset.setValue("Nginx", 31);
        dataset.setValue("IIS", 12);
        dataset.setValue("LiteSpeed", 2);
        dataset.setValue("Google server", 1);
        dataset.setValue("Others", 2);

        return dataset;
    }

    private JFreeChart createChart(DefaultPieDataset dataset) {

        JFreeChart pieChart = ChartFactory.createPieChart(
                "Web servers market share",
                dataset,
                false, true, false);

        return pieChart;
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new PieChartEx();
            ex.setVisible(true);
        });
    }
}

该示例使用饼图显示 Web 服务器的市场份额。

var dataset = new DefaultPieDataset();
dataset.setValue("Apache", 52);
dataset.setValue("Nginx", 31);
dataset.setValue("IIS", 12);
...

DefaultPieDataset 用于创建数据集。

JFreeChart barChart = ChartFactory.createPieChart(
        "Web servers market share",
        dataset,
        false, true, false);

使用 ChartFactory.createPieChart 方法创建一个新的饼图。

Servlet 中的 JFreeChart

在以下示例中,我们使用 Java servlet 创建饼图。该图表在 Web 浏览器中呈现。

com/zetcode/web/DoChart.java
package com.zetcode.web;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.data.general.DefaultPieDataset;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

@WebServlet(name = "DoChart", urlPatterns = {"/DoChart"})
public class DoChart extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        response.setContentType("image/png");

        OutputStream outputStream = response.getOutputStream();

        JFreeChart chart = getChart();
        int width = 500;
        int height = 350;

        ChartUtils.writeChartAsPNG(outputStream, chart, width, height);
    }

    public JFreeChart getChart() {

        var dataset = new DefaultPieDataset();
        dataset.setValue("Croatia", 22);
        dataset.setValue("Bohemia", 34);
        dataset.setValue("Bulgaria", 18);
        dataset.setValue("Spain", 5);
        dataset.setValue("Others", 21);

        JFreeChart chart = ChartFactory.createPieChart("Popular destinations",
                dataset, true, false, false);

        chart.setBorderVisible(false);

        return chart;
    }
}

DoChart servlet 创建一个饼图并将其发送给客户端。

response.setContentType("image/png");

setContentType 将内容设置为 PNG 图像。

OutputStream outputStream = response.getOutputStream();

通过 getOutputStream 方法,我们获得一个输出流。这是一个我们向其发送数据的通道。

ChartUtils.writeChartAsPNG(outputStream, chart, width, height);

ChartUtils.writeChartAsPNG 将二进制数据写入 outputstrem。

A pie chart in a browser
图:浏览器中的饼图

显示来自 MySQL 数据库的数据

JDBCCategoryDataset 是通过数据库 JDBC 结果集实现的 CategoryDataset。数据集通过调用带有字符串 SQL 查询的 executeQuery 来填充。

medals.sql
DROP TABLE IF EXISTS GoldMedalsLondon;

CREATE TABLE GoldMedalsLondon (
  Id int(11) NOT NULL AUTO_INCREMENT,
  Country text,
  Medals int(11) DEFAULT NULL,
  PRIMARY KEY (Id)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;

LOCK TABLES GoldMedalsLondon WRITE;
INSERT INTO GoldMedalsLondon VALUES (1,'USA',46),(2,'China',38),(3,'UK',29),
  (4,'Russia',22),(5,'South Korea',13),(6,'Germany',11);
UNLOCK TABLES;

我们在 MySQL 数据库表中有此数据。

mysql> SELECT * FROM GoldMedalsLondon;
+----+-------------+--------+
| Id | Country     | Medals |
+----+-------------+--------+
|  1 | USA         |     46 |
|  2 | China       |     38 |
|  3 | UK          |     29 |
|  4 | Russia      |     22 |
|  5 | South Korea |     13 |
|  6 | Germany     |     11 |
+----+-------------+--------+
6 rows in set (0.00 sec)

我们使用 mysql 工具显示数据。

com/zetcode/MySQLChartEx.java
package com.zetcode;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.jdbc.JDBCCategoryDataset;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MySQLChartEx {

    private static JDBCCategoryDataset dataset;

    public static void main(String[] args) throws IOException, SQLException {

        String url = "jdbc:mysql://:3306/testdb";
        String user = "root";
        String password = "s$cret";

        try (Connection con = DriverManager.getConnection(url, user, password)) {

            dataset = new JDBCCategoryDataset(con);
            dataset.executeQuery("SELECT Country, Medals FROM GoldMedalsLondon");
        }

        JFreeChart barChart = ChartFactory.createBarChart(
                "Olympic Gold medals in London",
                "",
                "Gold medals",
                dataset,
                PlotOrientation.VERTICAL,
                false, true, false);

        ChartUtils.saveChartAsPNG(new File("medals.png"), barChart, 450, 400);
    }
}

该示例从 MySQL 表中检索数据,创建一个柱状图,并将其保存到 PNG 图像中。

try (Connection con = DriverManager.getConnection(url, user, password)) {

    dataset = new JDBCCategoryDataset(con);
    dataset.executeQuery("SELECT Country, Medals FROM GoldMedalsLondon");
}

创建 JDBCCategoryDataset;它将数据库连接作为参数。

dataset.executeQuery("SELECT Country, Medals FROM GoldMedalsLondon");

executeQuery 通过针对现有数据库连接执行提供的查询来填充数据集。 SQL 查询必须至少返回两列。第一列是类别名称,其余列是值。

JFreeChart barChart = ChartFactory.createBarChart(
        "Olympic Gold medals in London",
        "",
        "Gold medals",
        dataset,
        PlotOrientation.VERTICAL,
        false, true, false);

创建柱状图。

ChartUtils.saveChartAsPNG(new File("medals.png"), barChart, 450, 400);\

柱状图使用 ChartUtils.saveChartAsPNG 方法保存到 PNG 文件。

来源

JFreeChart 文档

在本文中,我们使用 JFreeChart 在 Java 中创建了一些基本图表。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。

列出所有Java教程