贯通开源Web图形与报表技术全集
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第2篇 Web图形技术篇

第3章 JFreeChart开发全攻略

【本章导读】

本章由JFreeChart的介绍起步,讲述JFreeChart的安装与配置相关知识,为后续JFreeChart图形的开发准备环境,从而使读者更快地学习JFreeChart。

接着,笔者通过解读JFreeChart的源代码向读者详细讲述了JFreeChart核心类及其使用,对数据集(dataset)、图形(plot)、渲染器(renderer)、轴(Axis)和标题(Title)等的类图及每部分的核心类进行了详尽的描述,通过该节的学习,将使读者对JFreeChart中的核心模块有比较清晰的理解。

同时为了让读者能更好地将JFreeChart应用到实际项目中,笔者以丰富的开发案例向读者展示了在JFreeChart中如何开发各种图表,例如柱状图、饼图、时序图、甘特图和曲线图等,并重点强调了开发时需要注意的一些问题。

最后为了让读者对JFreeChart有更加深入的理解,笔者对JFreeChart的原理进行了初步分析,从而使得读者对JFreeChart这种开源Web图形技术具有更加深入的了解。

3.1 介绍

JFreeChart是开放源代码站点SourceForge.net上的一个Java项目,它主要用来生成各种各样的图表,这些图表包括:饼图、柱状图、曲线图、区域图、分布图、混合图、甘特图以及一些仪表盘等。

3.2 下载与配置JFreeChart

在使用JFreeChart技术开发图表时,首先需要下载JFreeChart,并需要进行一些必要的配置,本节将讲述如何下载JFreeChart和配置JFreeChart的知识,为后面的各节准备JFreeChart制作图表的开发环境。

3.2.1 下载JFreeChart

JFreeChart是JFreeChart公司在开源网站SourceForge.net上的一个项目,该公司的主要产品有如下:

● JFreeReport:报表解决工具(在本书的后续报表章节将会详细介绍);

● JFreeChart:Java图形解决方案(Application/Applet/Servlet/Jsp);

● JCommon:JFreeReport和JFreeChart的公共类库;

● JFreeDesigner:JFreeReport的报表设计工具。

JFreeChart主页:http://www.jfree.org/jfreechart/index.html

下载页面:http://sourceforge.net/project/showfiles.php?group_id=15494&package_id=12428

【提示】

至本书成稿之日,JFreeChart当前的最新版本为1.0.6,读者可以下载更新的版本。

读者可以将下载到的jfreechart-1.0.6.zip解开,其目录结构如图3-1所示。

图3-1 JFreeChart1.0.6的目录结构

各目录的描述信息如表3-1所示。

表3-1 JFreeChart各目录的描述信息

3.2.2 配置JFreeChart

若想在Web应用程序中应用JFreeChart,需在Web应用程序中进行相关配置。首先,读者可以在Eclipse中建立名为jfreechart的动态Web项目。接着修改工程的WEB-INF目录下的web.xml文件,在该文件中加入JFreeChart的相关设置,修改后的web.xml文件内容如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app 2 4.xsd">
    <servlet>
        <servlet-name>DisplayChart</servlet-name>
        <servlet-class>org.jfree.chart.servlet.DisplayChart</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DisplayChart</servlet-name>
        <url-pattern>/DisplayChart</url-pattern>
    </servlet-mapping>
</web-app>

同时将JFreeChart1.06的lib目录下的jar包复制到WEB-INF的lib目录中,这样就成功的在需要应用JFreeChart的Web项目中配置了JFreeChart。

3.3 核心类

为了更好地理解JFreeChart,更快地投入到JFreeChart的开发工作中去,通过JFreeChart的源码,对JFreeChart的核心类进行学习是很有必要的。

在本小节中,笔者将讲述JFreeChart的关键类图、核心类阐述及使用的知识,通过对JFreeChart中各种数据集(dataset)、图形(plot)、渲染器(renderer)、轴(axis)和标题(Title)等的类图及其中重要的类的描述,将使得读者更快地理解JFreeChart代码的结构,其中涉及相关概念等知识。

3.3.1 关键类图

在向读者介绍JFreeChart1.0.6的关键类图之前,首先向读者介绍一下JFreeChart1.0.6的包结构及各重要包的描述信息,如表3-2所示。

表3-2 包结构及重要包的描述信息

接下来讲解一下JFreeChart1.0.6的核心部分的类图。

1. 数据集dataset的类图

在JFreeChart中,与数据集相关的包都在org.jfree.data及其子包下。

(1)甘特图数据集的类图

甘特图数据集的类图如图3-2所示。

图3-2 甘特图数据集的类图

从图3-2中可以看出,甘特图的数据集TaskSeriesCollection类实现GanttCategoryDataset接口,并继承AbstractSeriesDataset类,GanttCategoryDataset接口继承IntervalCategoryDataset接口,抽象类AbstractSeriesDataset继承AbstractDataset抽象类(继承Dataset接口),接口Dataset和AbstractDataset分别是JFreeChart的数据集部分的重要接口和抽象类,在讲述其他类型的数据集时也会可看到与这些类中存在着紧密关系。

(2)饼图数据集的类图

在生成饼图时,数据集用的是DefaultPieDataset类,下面读者将学习饼图数据集的类图,如图3-3所示。

图3-3 饼图数据集的类图

饼图的数据集类图比甘特图的类图要简单得多,读者常用来制作饼图的DefaultPieDataset类继承了抽象类AbstractDataset,并实现了PieDataset接口。

(3)时序图数据集的类图

读者在后续的开发案例中将会发现,一般用TimeSeriesCollection类(在org.jfree.chart.data.time包下)来作为时序图的数据集。它与相关类的关系类图如图3-4所示。

图3-4 时序图数据集的类图

饼图的类图结构与甘特图的类图结构很相似,在此不再赘述。

(4)曲线图数据集的类图

曲线图在Web开发中是一种非常常见的图形,用来存放曲线图的数据集的类一般为XYDataset或XYSeriesCollection类,接着讲述曲线图的数据集类图。如图3-5所示。

图3-5 曲线图数据集的类图

比较时序图和饼图的类图后可以发现,TimeSeriesCollection类和XYSeriesCollection类都继承类AbstractIntervalXYDataset,并且都实现接口IntervalXYDatast。只是这两个类在有些细节处有一些不同。在后面的开发案例中调用方式也类似。

(5)柱状图数据集的类图

在本章3.4节的开发案例中读者可以看到,柱状图的数据集一般用的都是默认数据集DefaultCategoryDataset,柱状图与饼图一样是Web中最常用的图形,在Web中得到广泛的应用。图3-6展示了在JFreeChart中柱状图相关类之间的类图。

图3-6 柱状图数据集的类图

柱状图的类图相对比较简单,DefaultCategoryDataset类也像前面所说的各种数据集一样,继承自AbstractDataset抽象类,它直接实现接口CategoryDataset。

2. 图形Plot的类图

图形Plot在JFreeChart中是很重要的一个部分,通过Plot可以设置图形的各属性,例如背景色、透明度等信息。对于饼图、柱状图等在JFreeChart中都有不同的实现类,从源码中可以看出JFreeChart中图形plot的各类的关系图,如图3-7所示。

图3-7 图形plot的数据集的类图

在后面的案例中常用的CategoryPlot、XYPlot和PiePlot都继承自Plot类,而Plot类又实现了多个监听器。

3. 渲染renderer的类图

JFreeChart中的渲染在图形的显示效果方面有很大的作用。JFreeChart1.0.6中与渲染有关的类位于org.jfree.chart.renderer包或其子包下,渲染的相关类在JFreeChart中又有两类:一类是继承AbstractCategoryItemRenderer类的;而另一类是继承AbstractXYItemRenderer类的,继承AbstractCategoryItemRenderer类的类图如图3-8所示。

图3-8 渲染renderer的类图(一)

继承AbstractXYItemRenderer类的渲染类的类图如图3-9所示。

图3-9 渲染renderer的类图(二)

在上面的两个类图中,因为画图方便性的原因,部分不太常用的类没有画出来。

4. 轴axis的类图

在JFreeChart中绘制图形时,涉及到X轴、Y轴等与轴相关的类放在包org.jfree.chart.axis的目录下。在轴的实现类中,CategoryAxis、ValueAxis、DateAxis、NumberAxis和PeriodAxis,接下来看一下与轴相关类的类图,如图3-10所示。

图3-10 轴axis的类图

前面所述的5个常用类的功能描述如下:

CategoryAxis:一个显示种类的轴的实现,例如在显示柱状图时X轴的实现;

ValueAxis:是显示值数据的轴的基类,在默认情况下,值采用double,它的两个关键子类是DateAxis和NumberAxis,在3.4的开发案例中,曲线图中案例的Y轴可转换为ValueAxis;

DateAxis:显示日期轴的基类,在显示或者度量时采用的是整型或长整型数据,是从1970年1月1日计算器的毫秒数。当做标记的时候,这个毫秒数被转换为DateFormat实例类型,在3.4.3节时序图的案例中有用到DateAxis作为X轴,来显示时间;

NumberAxis:用来显示数字数据的轴,例如在3.4.5节区域图中有提到用NumberAxis作为区域图的Y轴;

PeriodAxis:显示基于org.jfree.data.time.RegularTimePeriod的日期范围。在后续的时序图的例子中有用到这种轴。

5. 标题Title的类图

在绘制各种图形时,都可以设置图形的标题和子标题等信息,所有标题部分在JFreeChart也是不可或缺的一部分,标题部分的类图如图3-11所示。

图3-11 标题Title的类图

TextTitle是最常用的标题,这几个类的功能描述如下:

Text:所有图形标题的基类。一个图形可以有多种标题,这些标题可以出现在图形的顶部、底部、左边和右边;

TextTitle:它能自动地包装成所想要的字符串并显示出来的图形标题;

ImageTitle:这是一个显示图片的标题。有时候显示图片标题是有用的,例如,当想要将一个logo作为显示的一个图片的页脚的时候;

LegendTitle:为图形中的数据显示一个图例的图形标题;

CompositeTitle:在一个BlockContainer中包含多个标题的图形标题;

DateTitle:显示日期的图形标题,该类继承TextTitle。

3.3.2 核心类阐述及使用

1. org.jfree.chart.ChartFactory

该类包括了一系列很方便的方法来创建标准类型的图形,例如,饼图、柱状图、区域图、甘特图、曲线图和时序图等。该类的常用方法及其描述如表3-3所示。

表3-3 org.jfree.chart.ChartFactory类的重要方法及其描述

在上表中,并没有列举ChartFacory类的所有方法,只是对常用的方法进行了说明,有需要的读者可以参考JFreeChart的源码,并可以通过修改该类的方法来创建自定义的图形类型。

2. org.jfree.chart.JFreeChart

该类调整了绘制图形的整个过程,其中的一个方法:public void draw(Graphics2D g2,Rectangle2D area);指示JFreeChart对象在图形设备的某个特定的区域上绘制图形。Java支持的图形设备有:屏幕、打印机和缓存的图片——通过对java.awt.Graphics2D的不同实现来实现这些功能。也正是归功于这种抽象,使得JFreeChart能够在这些目标设备的任何一种上生成图表,并且可以在一些第三方的实现上提供图表(例如,由Batik Project实现的SVG创建器)。

从主要的方面来说,JFreeChart类提供了绘制图形的上下文。图形从数据集中获得数据,并且可能代表单独的数据项的绘制,这主要取决于图形Plot类型(并不是所有的图形Plot都具备渲染器)。JFreeChart类能够与不同的Plot子类协同工作。根据Plot类型的不同,指定的数据集dataset也会有些不同。

接下来详细讲述一下JFreeChart类的一些重要方法,如表3-4所示。

表3-4 org.jfree.chart.JFreeChart的重要方法及其描述

3. org.jfree.data.general.Dataset

在3.3.2节中绘制数据集dataset的类图时,读者已经看到Dataset接口是数据集中的重要接口,它是所有数据集的基接口,从它本身来说,并没有什么用处,但是这个接口被PieDataset、CategoryDataset和SeriesDataset扩展。该接口定义的方法如表3-5所示。

表3-5 org.jfree.data.general.Dataset接口的重要方法及描述

4. org.jfree.chart.plot.CategoryPlot

CategoryPlot类是一个常用的绘图类,它常常用来显示柱状图,但它也支持曲线图、区域图等图形。一个CategoryPlot具有:

● 一个或多个X轴(CategoryAxis的实例);

● 一个或多个Y轴(ValueAxis的实例);

● 一个或多个数据集(可以是实现CategoryDataset接口的任何一个类的实例);

● 一个或多个渲染器(可以是实现CategoryItemRenderer接口的任何一个类的实例)。

图形可以在水平或垂直方向上显示。水平方向指的是X轴在图形的左边或右边,而Y轴在图形的底部或顶部,垂直方向的表述与此正好相反。

各属性的getter/setter方法不再讲述,其他的一些方法如表3-6所示。

表3-6 org.jfree.chart.plo.CategoryPlot除getter/setter方法外的其他方法及描述

使用举例如下:

CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot();.
categoryplot.setForegroundAlpha(0.5F);
categoryplot.setAxisOffset(new RectangleInsets(5D, 5D, 5D, 5D));
categoryplot.setBackgroundPaint(Color.lightGray);
categoryplot.setDomainGridlinesVisible(true);
categoryplot.setDomainGridlinePaint(Color.white);

上例的CategoryPlot对象由JFreeChart的getPlot()方法获得,在上例演示了CategoryPlot对象的部分方法的使用。

5. org.jfree.chart.plot.PiePlot

PiePlot类通过PieDataset接口的数据来绘制一个饼图。还有一个类PiePlot3D类是用来绘制带有3D效果的饼图的。该类的构造函数如下:

public PiePlot(PieDataset dataset);

通过一个给定的数据集来创建饼图。所有的属性都被初始化为默认的值,但是可以在任何时候改变这些属性值。该类的常用方法如表3-7所示。

表3-7 org.jfree.chart.plot.PiePlot类的常用方法及描述

PiePlot的使用举例如下:

PiePlot pieplot = (PiePlot) jfreechart.getPlot();
//把"中级程序员"的那一块”挖“出来50%
pieplot.setExplodePercent("中级程序员", 0.5D);
//显示各项所占的百分比
pieplot.setCircular(true);
pieplot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} = {2}",
              NumberFormat.getNumberInstance(),
              NumberFormat.getPercentInstance()));

上例的PiePlot对象由JFreeChart的getPlot()方法获得,在上例中演示了setExplodePercent(…)、setCircular(…)和setLabelGenerator(…)方法的使用,定义了需要显示各项的百分比,并定义了显示的格式和特别需要突出显示的一块。

6. org.jfree.chart.plot.XYPlot

绘制来自XYDataset的一个可视化的表现。X轴度量x的值,Y轴度量y的值。图形的类型通常是垂直方向的,但是在某些应用程序中也可以调整为水平方向的。

XYPlot能具有0到多个数据集,并且每一个数据集能够与一个渲染器关联起来。这个数据集可以是实现XYDataset接口的任何一个类的实例,而渲染其可以是实现XYItemRenderer接口的任何一个类的实例。该类具有数据集dataset和渲染器renderer的getter/setter方法。该类的常用方法如表3-8所示。

表3-8 org.jfree.chart.plot.XYPlot类的常用方法及描述

XYPlot的使用举例如下:

XYPlot xyplot = (XYPlot)jfreechart.getPlot();
xyplot.getDomainAxis().setLowerMargin(0.0D);
xyplot.getDomainAxis().setUpperMargin(0.0D);

7.org.jfree.chart. renderer.category.BarRenderer

该渲染器用来让CategoryPlot通过CategoryDataset中的数据来创建柱状图。这个渲染器将会在水平或垂直方向中处理图形,实现CategoryItemRenderer接口,并且在基类AbstarctCategoryItem Renderer的基础上扩展。构造函数如下:

public BarRenderer();

该构造函数创建一个带有默认设置的渲染器。在默认情况下,渲染器绘制柱子的外边框,柱子间的距离为20%。该类的常用方法如表3-9所示。

表3-9 org.jfree.chart.renderer.category.BarRenderer类的常用方法及描述

ChartFactory类在创建柱状图时使用的是这个渲染器。该类的使用举例如下:

BarRenderer barrenderer = (BarRenderer)categoryplot.getRenderer();
barrenderer.setDrawBarOutline(false);
GradientPaint gradientpaint = new GradientPaint(0.0F, 0.0F, Color.blue, 0.0F, 0.0F, new Color(0, 0, 64));
GradientPaint gradientpaint1 =new GradientPaint(0.0F, 0.0F, Color.green, 0.0F, 0.0F, new Color(0, 64, 0));
GradientPaint gradientpaint2 = new GradientPaint(0.0F, 0.0F, Color.red, 0.0F, 0.0F, new Color(64, 0, 0));
barrenderer.setSeriesPaint(0, gradientpaint);
barrenderer.setSeriesPaint(1, gradientpaint1);
barrenderer.setSeriesPaint(2, gradientpaint2);

8. org.jfree.chart.axis.Axis

该类是各种轴的抽象类,Plot的子类,包括CategoryPlot类和XYPlot类,都使用轴来显示数据。各种轴组成的类图请参见3.3.2节。Axis的属性如表3-10所示。

表3-10 org.jfree.chart.axis.Axis的重要属性及描述

关于这些属性的getter/setter方法,在此不再赘述,只讲一下另外的一些方法,如表3-17所示。

9. org.jfree.chart.axis.CategoryAxis

CategoryAxis能够在CategoryPlot中用作X轴。种类以一种规则的方式在轴上呈现,在第一个种类前、最后一个种类后和每个种类之间都有间隔。轴常常用来显示每一个种类之间的标记。控制位置、对齐方式等有很多选项可供选择。

该类的使用举例如下:

CategoryAxis categoryaxis = categoryplot.getDomainAxis();
categoryaxis.setCategoryLabelPositions(CategoryLabelPositions.UP 45);
categoryaxis.setLowerMargin(0.0D);
categoryaxis.setUpperMargin(0.0D);
categoryaxis.addCategoryLabelToolTip("种类1", "第一个种类.");

10. org.jfree.chart.title.Title

该类是所有图形标题的基类,已经实现了很多子类,包括TextTitle、DateTitle、LegendTitle和ImageTitle。该类继承AbstractBlock抽象类,因此具有该抽象类的margin、border和padding属性。11.org.jfree.chart.title.TextTitle

该类为Title的子类,这个图形标题显示的是字符串。构造函数如下:

public TextTitle(String text);

作用是通过指定的文本text创建一个图形标题。默认情况下,标题位于图形的顶部,水平居中,默认的字体为SansSerif,12pt,加粗,字体颜色默认为黑色。该类的常用方法如表3-11所示。

表3-11 org.jfree.chart.title.TextTitle类的常用方法及描述

使用举例如下:

TextTitle texttitle = new TextTitle("标题举例.");

texttitle.setFont(new Font("SansSerif", 0, 12));
texttitle.setPosition(RectangleEdge.TOP);
texttitle.setPadding(new RectangleInsets(UnitType.RELATIVE,
                      0.05D,
                      0.05D,
                      0.05D,
                      0.05D));
texttitle.setVerticalAlignment(VerticalAlignment.BOTTOM);
jfreechart.addSubtitle(texttitle);

在上例中,首先通过TextTitle的构造函数创建了一个TextTitle实例,并且设置它的各个属性,并将设置好的对象作为JFreeChart对象的标题。

3.4 开发案例

继上节讲述完JFreeChart的核心类之后,本节将开始学习如何使用JFreeChart来进行开发,将讲述如何使用JFreeChart来制作各种类型的图形,包括柱状图、饼图、时序图、甘特图和组合图等。

本节将通过丰富的实例展示JFreeChart的使用以及各种核心类的使用方法。

3.4.1 柱状图

首先,读者可以在名为jfreechart的工程的WebRoot目录下建立bar目录,用来存放各柱状图的实例。读者可在bar目录下建立名为bar1的jsp文件,bar1.jsp为柱状图的简单示例,代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.plot.PlotOrientation,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.data.category.DefaultCategoryDataset"%>
<%
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(110, "长沙", "白菜");
dataset.addValue(220, "长沙", "生菜");
dataset.addValue(330, "长沙", "西洋菜");
dataset.addValue(440, "长沙", "豆芽菜");

JFreeChart chart = ChartFactory.createBarChart3D("蔬菜类销量统计图",  "蔬菜类", "销量",
                dataset, PlotOrientation.VERTICAL,  false, false, false);
String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="500" height="300" border="0" usemap="#<%= filename %>">

从以上代码可以看出,在此创建了四个不同的柱子,分别来表示长沙的白菜、生菜、西洋菜和豆芽菜的销量情况。

部署该Web应用程序后,在浏览器中输入地址:http://localhost:8080/jfreechart/bar/bar1.jsp,运行效果如图3-12所示。

图3-12 简单、单调的柱状图实例

从图3-12可以看出,图中的四根柱子都是通过同一种颜色表现出来的,颜色稍显单调,为了对这四种不同类型进行区分,查看图表的人可能更希望通过不同颜色来表示不同的类型,此时可以在WebRoot/bar目录下建立bar2.jsp文件,在该文件中,通过不同颜色来显示不同类型。内容如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.plot.PlotOrientation,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.data.category.CategoryDataset,
              org.jfree.data.general.DatasetUtilities,
              "%>
<%
double[][] data = new double[][] {{1310}, {720}, {1130}, {440}};
String[] rowKeys = {"白菜", "生菜","西洋菜", "豆芽菜"};
String[] columnKeys = {""};
CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data);

JFreeChart chart = ChartFactory.createBarChart3D("广州蔬菜销量统计图",  "蔬菜", "销量",
                dataset, PlotOrientation.VERTICAL,true, false, false);


String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/bar/bar2.jsp,该例的运行效果如图3-13所示。

图3-13 用不同颜色表示不同柱子的柱状图示例

到这里后,读者可能又会有疑问,在很多网站中,有时候需要比较不同城市间的各种蔬菜的销量情况,这种图在JFreeChart中怎么展示呢?接着建立bar3.jsp来展示这种效果,内容如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.plot.PlotOrientation,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.data.category.CategoryDataset,
              org.jfree.data.general.DatasetUtilities"%>
<%
double[][] data = new double[][] {{1310, 1220, 1110, 1000},
    {720, 700, 680, 640},
    {1130, 1020,980, 800},
    {440, 400, 360, 300}};
String[] rowKeys = {"白菜", "生菜","西洋菜", "豆芽菜"};
String[] columnKeys = {"长沙", "湘潭", "衡阳", "岳阳"};
CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data);

JFreeChart chart = ChartFactory.createBarChart3D("蔬菜销量统计图",  "蔬菜", "销量",
                dataset, PlotOrientation.VERTICAL,true, false, false);
String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="500" height="300" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/bar/bar3.jsp,运行效果如图3-14所示。

图3-14 用柱状图表示蔬菜销量统计情况

在bar3.jsp的代码中,{1310, 1220, 1110, 1000}中四个数字分别表示白菜在长沙、湘潭、衡阳和岳阳这4个城市的销量,其他依此类推。

细心的读者可能会看到,在上图的运行效果中,每根柱子中并没有具体的数值的信息,为了看到在柱子上标出具体数值的效果,读者在WebRoot/bar目录下建立bar4.jsp,内容如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.plot.PlotOrientation,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.data.category.CategoryDataset,
              org.jfree.data.general.DatasetUtilities,
              org.jfree.chart.plot.*,
              org.jfree.chart.labels.*,
              org.jfree.chart.renderer.category.BarRenderer3D,
              java.awt.*,
              org.jfree.ui.*,
              org.jfree.chart.axis.AxisLocation"%>
<%
double[][] data = new double[][] {{1310, 1220, 1110, 1000},
    {720, 700, 680, 640},
    {1130, 1020,980, 800},
    {440, 400, 360, 300}};
String[] rowKeys = {"白菜", "生菜","西洋菜", "豆芽菜"};
String[] columnKeys = {"长沙", "湘潭", "衡阳", "岳阳"};
CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data);
JFreeChart chart = ChartFactory.createBarChart3D("蔬菜销量统计图",  "蔬菜", "销量",
                dataset, PlotOrientation.VERTICAL,true, true, false);
CategoryPlot plot = chart.getCategoryPlot();
//设置网格背景颜色
plot.setBackgroundPaint(Color.white);
//设置网格竖线颜色
plot.setDomainGridlinePaint(Color.pink);
//设置网格横线颜色
plot.setRangeGridlinePaint(Color.pink);
//显示每个柱的数值,并修改该数值的字体属性
BarRenderer3D renderer = new BarRenderer3D();
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setBaseItemLabelsVisible(true);
//默认的数字显示在柱子中,通过如下两句可调整数字的显示
//注意:此句很关键,若无此句,那数字的显示会被覆盖,给人数字没有显示出来的问题
renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition
                                          (ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE LEFT));
renderer.setItemLabelAnchorOffset(10D);
//设置每个地区所包含的平行柱的之间距离
renderer.setItemMargin(0.3);
plot.setRenderer(renderer);
//设置地区、销量的显示位置
//将下方的“肉类”放到上方
plot.setDomainAxisLocation(AxisLocation.TOP OR RIGHT);
//将默认放在左边的“销量”放到右方
plot.setRangeAxisLocation(AxisLocation.BOTTOM OR RIGHT);
String filename = ServletUtilities.saveChartAsPNG(chart, 700, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="700" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入如下地址:http://localhost:8080/jfreechart/bar/bar4.jsp,运行效果如图3-15所示。

图3-15 在柱状图的柱子上显示具体数值的柱状图示例

在JFreeChart的柱状图基础上读者还可以进行扩展,可以制造圆柱等形状的柱子。

3.4.2 饼图

在日常的开发过程中,饼图一般用来表示各部分所占比例的情况,它在各网站上应用广泛,在本节中笔者将带领读者进入饼图的奇妙世界中去。

首先,读者可以在工程的WebRoot目录下建立一个名为pie的目录,用来存放本小节中的各饼图示例。接着建立pie1.jsp文件,笔者将通过这个简单的案例带领读者进入饼图的开发。该例的饼图的数据集采用的是org.jfree.data.general.DefaultPieDataset类,JFreeChart对象是通过org.jfree.chart.ChartFactory的createPieChart3D(…)方法生成的,该文件的代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.*,
              org.jfree.chart.plot.PiePlot,
              org.jfree.data.general.DefaultPieDataset,
              org.jfree.chart.servlet.ServletUtilities,
              java.awt.*,
              org.jfree.chart.labels.StandardPieSectionLabelGenerator,
              java.text.NumberFormat"%>
<%
//设置数据集
DefaultPieDataset dataset = new DefaultPieDataset();
dataset.setValue("初级程序员", 10000);
dataset.setValue("中级程序员", 23000);
dataset.setValue("高级程序员", 10000);
dataset.setValue("项目经理", 6000);
dataset.setValue("系统分析师", 6000);
dataset.setValue("软件架构师", 3000);
dataset.setValue("其他", 10000);

//通过工厂类生成JFreeChart对象
JFreeChart chart = ChartFactory.createPieChart3D("IT行业职业分布图", dataset, true, false, false);
PiePlot pieplot = (PiePlot) chart.getPlot();
pieplot.setLabelFont(new Font("宋体", 0, 12));
//没有数据的时候显示的内容
pieplot.setNoDataMessage("无数据显示");

String filename = ServletUtilities.saveChartAsPNG(chart, 700, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="700" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/pie/pie1.jsp,运行效果如图3-16所示。

图3-16 用饼图表示IT行业职业分布的示例

读者在此处可能会发出疑问:这里定义了各块所占的值,上图中也根据各块所占的比率进行了相应显示,但看不到所占的百分比,在JFreeChart中是否可以把百分比的值也显示出来呢?读者只需要在上例的代码:pieplot.setNoDataMessage("无数据显示");的后面加上如下几句即可:

//在饼图的各块中显示百分比
pieplot.setCircular(true);
pieplot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} = {2}",
    NumberFormat.getNumberInstance(),
    NumberFormat.getPercentInstance()));

其中{0}表示的是初级程序员、中级程序员、高级程序员等内容,{2}表示的是所占的百分比,{1}在此例中未提及,它表示的是各项的值,例如10000、23000、10000等。

StandardPieSectionLabelGenerator构造函数的第二个参数表示的是各项的具体数值(即{1})的输出格式,在上面的代码中,NumberFormat.getNumberInstance()表示的是照数字的原样输出,若将NumberFormat.getNumberInstance()改成new DecimalFormat("0.00")则表示将数字保留两位小数,分别变成10000.00、23000.00、10000.00等。第三个参数表示的是各项所占的百分比的格式,上面代码中的NumberFormat.getPercentInstance()表示各项的百分比显示为带百分号的方式,如15%、34%等。若将NumberFormat.getPercentInstance()改为:new DecimalFormat("0.00"),则百分比显示变成0.15、0.34等。

此时在浏览器中输入地址:http://localhost:8080/jfreechart/pie/pie1.jsp,运行效果如图3-17所示。

图3-17 显示各项具体比例的饼图示例

在JFreeChart自带的demo中,读者可以看到一些将某一块挖出来的效果,这在需要突出显示某块的情况下很有用。在实现这个功能时,主要用到setExplodePercent(…)方法,该方法将指定名称的某块挖出来,以便能突出显示。

首先,读者在WebRoot/pie目录下建立名为pie2的jsp文件,该例还是通过修改pie1.jsp文件来实现,代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.*,
              org.jfree.chart.plot.PiePlot,
              org.jfree.data.general.DefaultPieDataset,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.chart.labels.StandardPieSectionLabelGenerator,
              java.text.NumberFormat"%>
<%
//设置数据集
DefaultPieDataset dataset = new DefaultPieDataset();
dataset.setValue("初级程序员", 10000);
dataset.setValue("中级程序员", 23000);
dataset.setValue("高级程序员", 10000);
dataset.setValue("项目经理", 6000);
dataset.setValue("系统分析师", 6000);
dataset.setValue("软件架构师", 3000);
dataset.setValue("其他", 10000);

JFreeChart chart = ChartFactory.createPieChart("IT行业职业分布图", dataset, true, false, false);
PiePlot pieplot = (PiePlot) chart.getPlot();
pieplot.setNoDataMessage("无数据显示");

//把"中级程序员" 的那一块”挖“出来50%
pieplot.setExplodePercent("中级程序员", 0.5D);
//显示各项所占的百分比
pieplot.setCircular(true);
pieplot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} = {2}",
          NumberFormat.getNumberInstance(),
          NumberFormat.getPercentInstance()));

String filename = ServletUtilities.saveChartAsPNG(chart, 700, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="700" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/pie/pie2.jsp,运行效果如图3-18所示。

图3-18 将某块突出显示的饼图示例

由图3-18可以看出,代码pieplot.setExplodePercent("中级程序员", 0.5D);的作用是将“中级程序员”这一块挖出了50%,可以通过代码pieplot.setExplodePercent(1, 0.5D);来实现同样的效果,此时第一个参数为int类型,从0开始计数,故1表示的是第二块(即“中级程序员”),但是,该方法在1.0.6中已是不赞成使用的方法,最好还是采用笔者所说的第一种方式。

JFreeChart的饼图还可以做到非常炫的效果,例如水晶的漂亮效果,读者在WebRoot/pie目录下建立名为pie3的jsp文件。PiePlot3D类的setForegroundAlpha(…)方法可以设置3D图的透明图,利用org.jfree.chart.ChartFactory的createPieChart3D(…)可以创建3维的JFreeChart的饼图对象,该jsp文件的代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.*,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.util.Rotation,
              org.jfree.data.general.DefaultPieDataset,
              org.jfree.chart.plot.PiePlot3D"%>
<%
//设置数据集
DefaultPieDataset dataset = new DefaultPieDataset();
dataset.setValue("初级程序员", 10000);
dataset.setValue("中级程序员", 23000);
dataset.setValue("高级程序员", 10000);
dataset.setValue("项目经理", 6000);
dataset.setValue("系统分析师", 6000);
dataset.setValue("软件架构师", 3000);
dataset.setValue("其他", 10000);

//通过工厂类生成JFreeChart对象
JFreeChart chart = ChartFactory.createPieChart3D("IT行业职业分布图", dataset, true, true, false);
//获得3D的水晶饼图对象
PiePlot3D pieplot3d = (PiePlot3D) chart.getPlot();
//设置开始角度
pieplot3d.setStartAngle(150D);
//设置方向为”顺时针方向“
pieplot3d.setDirection(Rotation.CLOCKWISE);
//设置透明度,0.5F为半透明,1为不透明,0为全透明
pieplot3d.setForegroundAlpha(0.5F);
pieplot3d.setNoDataMessage("无数据显示");

String filename = ServletUtilities.saveChartAsPNG(chart, 700, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="700" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/pie/pie3.jsp,运行效果如图3-19所示。

图3-19 水晶饼图示例

3.4.3 时序图

时序图在网页上应用广泛,例如,可以用它来展示股票的分时图、博客访问量统计情况、某业务的使用统计情况等,时序图是各图表中的重中之重。在本小节中,笔者将通过多个案例来向读者详细展示如何利用JFreeChart画时序图。

首先,读者在WebRoot下建立一个名为time_series的目录,用来存放本小节中的时序图案例。首先看一个时序图的简单例子,时序图的JFreeChart对象是通过org.jfree.chart.ChartFactory类的createTimeSeriesChart(…)方法来创建。在WebRoot/time_series下建立名为time_series1的jsp文件,笔者在该页面中编写了一个统计xx网站流量的例子,统计该网站在2006年度的流量走势,该JSP页面的代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import = "org.jfree.chart.ChartFactory,
                  org.jfree.chart.JFreeChart,
                  org.jfree.chart.servlet.ServletUtilities,
                  org.jfree.chart.title.TextTitle,
                  org.jfree.data.time.TimeSeries,
                  org.jfree.data.time.Month,
                  org.jfree.data.time.TimeSeriesCollection,
                  java.awt.Font"%>
<%
//流量统计时间线
TimeSeries timeSeries = new TimeSeries("xx网站流量统计", Month.class);
//时间曲线数据集合
TimeSeriesCollection lineDataset = new TimeSeriesCollection();

//构造数据集合
timeSeries.add(new Month(1, 2006), 19300);
timeSeries.add(new Month(2, 2006), 39390);
timeSeries.add(new Month(3, 2006), 33990);
timeSeries.add(new Month(4, 2006), 45533);
timeSeries.add(new Month(5, 2006), 38799);
timeSeries.add(new Month(6, 2006), 41000);
timeSeries.add(new Month(7, 2006), 37899);
timeSeries.add(new Month(8, 2006), 42999);
timeSeries.add(new Month(9, 2006), 50383);
timeSeries.add(new Month(10, 2006), 87666);
timeSeries.add(new Month(11, 2006), 57897);
timeSeries.add(new Month(12, 2006), 69399);

lineDataset.addSeries(timeSeries);
JFreeChart chart = ChartFactory.createTimeSeriesChart("流量统计时间线", "月份", "访问量", lineDataset, true,
true, true);

//设置子标题
TextTitle subtitle = new TextTitle("2006年度", new Font("黑体", Font.BOLD, 12));
chart.addSubtitle(subtitle);

//设置主标题
chart.setTitle(new TextTitle("xx网站流量统计", new Font("隶书", Font.ITALIC, 15)));
chart.setAntiAlias(true);
String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="500" height="300" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/time_series/time_series1.jsp,运行效果如图3-20所示。

图3-20 用时序图展示网站流量统计情况

上图中通过曲线展示了流量在各月的走势,但在上图中,并没有显示所采的点的具体数值,若想要显示数值,还需对上例进行部分修改。读者在WebRoot/time_series目录下建立名为time_series2的jsp文件,该文件将针对上例进行部分修改,来显示所采点的具体数值。修改后的time_series2.jsp的内容如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import = "org.jfree.chart.ChartFactory,
                  org.jfree.chart.title.TextTitle,
                  org.jfree.data.time.TimeSeries,
                  org.jfree.data.time.Month,
                  org.jfree.data.time.TimeSeriesCollection,
                  org.jfree.chart.plot.XYPlot,
                  org.jfree.chart.renderer.xy.XYLineAndShapeRenderer,
                  java.awt.Color,
                  org.jfree.ui.RectangleInsets,
                  java.awt.Font,
                  org.jfree.chart.renderer.xy.XYItemRenderer,
                  org.jfree.chart.JFreeChart,
                  org.jfree.chart.servlet.ServletUtilities,
                  org.jfree.chart.labels.*,
                  org.jfree.ui.*"%>
<%
//流量统计时间线
TimeSeries timeSeries = new TimeSeries("xx网站流量统计", Month.class);
//时间曲线数据集合
TimeSeriesCollection lineDataset = new TimeSeriesCollection();

//构造数据集合
timeSeries.add(new Month(1, 2006), 19300);
timeSeries.add(new Month(2, 2006), 39390);
timeSeries.add(new Month(3, 2006), 33990);
timeSeries.add(new Month(4, 2006), 45533);
timeSeries.add(new Month(5, 2006), 38799);
timeSeries.add(new Month(6, 2006), 41000);
timeSeries.add(new Month(7, 2006), 37899);
timeSeries.add(new Month(8, 2006), 42999);
timeSeries.add(new Month(9, 2006), 50383);
timeSeries.add(new Month(10, 2006), 87666);
timeSeries.add(new Month(11, 2006), 57897);
timeSeries.add(new Month(12, 2006), 69399);

lineDataset.addSeries(timeSeries);
JFreeChart chart = ChartFactory.createTimeSeriesChart("流量统计时间线", "月份", "访问量", lineDataset, true,
true, true);

XYPlot plot = (XYPlot) chart.getPlot();
XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer)plot.getRenderer();
//设置网格背景颜色
plot.setBackgroundPaint(Color.white);
//设置网格竖线颜色
plot.setDomainGridlinePaint(Color.pink);
//设置网格横线颜色
plot.setRangeGridlinePaint(Color.pink);
//设置曲线图与xy轴的距离
plot.setAxisOffset(new RectangleInsets(0D, 0D, 0D, 10D));
//设置曲线是否显示数据点
xylineandshaperenderer.setBaseShapesVisible(true);
//设置曲线显示各数据点的值
XYItemRenderer xyitem = plot.getRenderer();
xyitem.setBaseItemLabelsVisible(true);
xyitem.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12,
                                                                      TextAnchor.BASELINE LEFT));
xyitem.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator());
xyitem.setBaseItemLabelFont(new Font("Dialog", 1, 14));
plot.setRenderer(xyitem);
//设置子标题
TextTitle subtitle = new TextTitle("2006年度", new Font("黑体", Font.BOLD, 12));
chart.addSubtitle(subtitle);
//设置主标题
chart.setTitle(new TextTitle("xx网站流量统计", new Font("隶书", Font.ITALIC, 15)));
chart.setAntiAlias(true);
String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="500" height="300" border="0" usemap="#<%= filename %>">

从上面的代码中可以看出,代码xylineandshaperenderer.setBaseShapesVisible(true);就是用来设置时序图显示各采样数据点的。

在浏览器中输入地址:http://localhost:8080/jfreechart/time_series/time_series2.jsp,运行效果如图3-21所示。

图3-21 显示曲线中点的具体数值的时序图示例

读者在开发过程中可能还会遇到将两个趋势线显示在一个图表中,进行对比分析的情况,在WebRoot/time_series目录下建立名为time_series3的jsp文件,修改其内容如下所示:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import = "org.jfree.chart.ChartFactory,
                  org.jfree.chart.JFreeChart,
                  org.jfree.chart.servlet.ServletUtilities,
                  org.jfree.data.time.TimeSeries,
                  org.jfree.data.time.Month,
                  org.jfree.data.time.TimeSeriesCollection,
                  java.awt.*,
                  java.text.SimpleDateFormat,
                  java.util.TimeZone,
                  org.jfree.chart.axis.PeriodAxis,
                  org.jfree.chart.axis.PeriodAxisLabelInfo,
                  org.jfree.chart.plot.XYPlot,
                  org.jfree.chart.renderer.xy.XYLineAndShapeRenderer,
                  org.jfree.data.time.*,
                  org.jfree.ui.*"%>
<%
TimeSeries timeseries = new TimeSeries("XX图书销量", org.jfree.data.time.Month.class);
timeseries.add(new Month(2, 2006), 456);
timeseries.add(new Month(3, 2006), 367);
timeseries.add(new Month(4, 2006), 353);
timeseries.add(new Month(5, 2006), 368);
timeseries.add(new Month(6, 2006), 260);
timeseries.add(new Month(7, 2006), 248);
timeseries.add(new Month(8, 2006), 154);
timeseries.add(new Month(9, 2006), 134);
timeseries.add(new Month(10, 2006), 145);
timeseries.add(new Month(11, 2006), 130);
timeseries.add(new Month(12, 2006), 139);
timeseries.add(new Month(1, 2007), 138);
timeseries.add(new Month(2, 2007), 150);
timeseries.add(new Month(3, 2007), 137);
timeseries.add(new Month(4, 2007), 131);
timeseries.add(new Month(5, 2007), 139);
timeseries.add(new Month(6, 2007), 137);
timeseries.add(new Month(7, 2007), 132);

TimeSeries timeseries1 = new TimeSeries("YY图书销量", org.jfree.data.time.Month.class);
timeseries1.add(new Month(2, 2006), 537);
timeseries1.add(new Month(3, 2006), 499);
timeseries1.add(new Month(4, 2006), 443);
timeseries1.add(new Month(5, 2006), 440);
timeseries1.add(new Month(6, 2006), 323);
timeseries1.add(new Month(7, 2006), 289);
timeseries1.add(new Month(8, 2006), 246);
timeseries1.add(new Month(9, 2006), 250);
timeseries1.add(new Month(10, 2006), 198);
timeseries1.add(new Month(11, 2006), 207);
timeseries1.add(new Month(12, 2006), 211);
timeseries1.add(new Month(1, 2007), 197);
timeseries1.add(new Month(2, 2007), 189);
timeseries1.add(new Month(3, 2007), 203);
timeseries1.add(new Month(4, 2007), 167);
timeseries1.add(new Month(5, 2007), 156);
timeseries1.add(new Month(6, 2007), 188);
timeseries1.add(new Month(7, 2007), 200);
TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
timeseriescollection.addSeries(timeseries);
timeseriescollection.addSeries(timeseries1);
timeseriescollection.setXPosition(TimePeriodAnchor.MIDDLE);

JFreeChart jfreechart = ChartFactory.createTimeSeriesChart("XX与YY图书销量对比",
                                        "Date", "销量(单位:本)", timeseriescollection, true, true, false);



XYPlot xyplot = (XYPlot)jfreechart.getPlot();
xyplot.setBackgroundPaint(Color.lightGray);
xyplot.setDomainGridlinePaint(Color.white);
xyplot.setRangeGridlinePaint(Color.white);
xyplot.setAxisOffset(new RectangleInsets(5D, 5D, 5D, 5D));
xyplot.setDomainCrosshairVisible(true);
xyplot.setRangeCrosshairVisible(true);
org.jfree.chart.renderer.xy.XYItemRenderer xyitemrenderer = xyplot.getRenderer();
if (xyitemrenderer instanceof XYLineAndShapeRenderer) {
    XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer)xyitemrenderer;
    xylineandshaperenderer.setShapesVisible(true);
    xylineandshaperenderer.setShapesFilled(true);
    xylineandshaperenderer.setBaseItemLabelsVisible(true);
}
PeriodAxis periodaxis = new PeriodAxis("日期");
periodaxis.setTimeZone(TimeZone.getTimeZone("Pacific/Auckland"));
periodaxis.setAutoRangeTimePeriodClass(org.jfree.data.time.Month.class);
periodaxis.setMajorTickTimePeriodClass(org.jfree.data.time.Month.class);
PeriodAxisLabelInfo aperiodaxislabelinfo[] = new PeriodAxisLabelInfo[2];
aperiodaxislabelinfo[0] = new PeriodAxisLabelInfo(org.jfree.data.time.Month.class,
                      new SimpleDateFormat("MMM"), new RectangleInsets(2D, 2D, 2D, 2D),
                      new Font("SansSerif", 1, 10), Color.blue,
                      false, new BasicStroke(0.0F), Color.lightGray);
aperiodaxislabelinfo[1] = new PeriodAxisLabelInfo(org.jfree.data.time.Year.
                      class, new SimpleDateFormat("yyyy"));
periodaxis.setLabelInfo(aperiodaxislabelinfo);
xyplot.setDomainAxis(periodaxis);
String filename = ServletUtilities.saveChartAsPNG(jfreechart, 600, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="600" height="400" border="0" usemap="#<%= filename %>">

在浏览器输入地址:http://localhost:8080/jfreechart/time_series/time_series3.jsp,运行效果如图3-22所示。

图3-22 用多条时序图显示对比情况示例

观察代码后可以发现,在该例中,通过如下代码将一根根曲线图加入到图表中去:

TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
timeseriescollection.addSeries(timeseries);
timeseriescollection.addSeries(timeseries1);

由此可知,若为三者或三者以上时,也可以使用此种方法将其加入到TimeSeriesCollection对象中。

3.4.4 甘特图

甘特图一般用来表示项目的进度等信息,在项目管理中应用广泛,甘特图可以用很多工具绘制,例如Office的project等。在JFreeChart中也提供了绘制甘特图的方法。

首先,读者在工程的WebRoot目录下建立gantt目录,用来存放本小节的甘特图案例。接着在WebRoot/gantt目录下建立名为gantt1.jsp的文件,在该文件中将向读者演示一个JFreeChart制造甘特图的简单案例。这个例子展示的是软件开发过程的各步骤实际时间相对于计划时间的提前或推后情况。

首先,需要建立两个任务序列,分别存放在计划和实际情况下的各任务,新建任务对象时,需要给出该任务的开始时间和完成时间。计划和实际情况下的任务序列建立好后,将这两者都加入到TaskSeriesCollection型对象中,接着将该TaskSeriesCollection型对象作为参数传入到org.jfree.chart.ChartFactory的createGanttChart(…)方法中,因此来建立一个JFreeChart型对象。gantt1.jsp文件的内容如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.chart.plot.CategoryPlot,
              org.jfree.chart.renderer.category.GanttRenderer,
              org.jfree.data.gantt.*,
              org.jfree.data.time.SimpleTimePeriod,
              gantt.DateUtil"%>
<%
//建立计划情况下的任务序列,每个任务都包括名称、开始时间和结束时间信息
TaskSeries taskseries =new TaskSeries("计划");
taskseries.add(new Task("需求获取", new SimpleTimePeriod(DateUtil.date(2006, 3, 6),
                                                                        DateUtil.date(2006, 3, 9))));
taskseries.add(new Task("需求分析", new SimpleTimePeriod(DateUtil.date(2006, 3, 10),
                                                                        DateUtil.date(2006, 3, 10))));
taskseries.add(new Task("系统设计", new SimpleTimePeriod(DateUtil.date(2006, 4, 6),
                                                                        DateUtil.date(2006, 4, 30))));
taskseries.add(new Task("设计评审", new SimpleTimePeriod(DateUtil.date(2006, 5, 2),
                                                                        DateUtil.date(2006, 5, 2))));
taskseries.add(new Task("设计实现", new SimpleTimePeriod(DateUtil.date(2006, 5, 3),
                                                                       DateUtil.date(2006, 8, 12))));
taskseries.add(new Task("测试", new SimpleTimePeriod(DateUtil.date(2006, 8, 13),
                                                                       DateUtil.date(2006, 9, 31))));

//建立实际情况下的任务序列,每个任务都包括名称、开始时间和结束时间信息
TaskSeries taskseries1 =new TaskSeries("实际");
taskseries1.add(new Task("需求获取", new SimpleTimePeriod(DateUtil.date(2006, 3, 6),
                                                                        DateUtil.date(2006, 3, 9))));
taskseries1.add(new Task("需求分析", new SimpleTimePeriod(DateUtil.date(2006, 3, 10),
                                                                       DateUtil.date(2006, 4, 15))));
taskseries1.add(new Task("系统设计", new SimpleTimePeriod(DateUtil.date(2006, 4, 15),
                                                                       DateUtil.date(2006, 5, 17))));
taskseries1.add(new Task("设计评审", new SimpleTimePeriod(DateUtil.date(2006, 5, 30),
                                                                       DateUtil.date(2006, 5, 30))));
taskseries1.add(new Task("设计实现", new SimpleTimePeriod(DateUtil.date(2006, 6, 1),
                                                                       DateUtil.date(2006, 9, 30))));
taskseries1.add(new Task("测试", new SimpleTimePeriod(DateUtil.date(2006, 9, 31),
                                                                      DateUtil.date(2006, 10, 17))));

//将计划和实际情况下的两个任务序列加入到TaskSeriesCollection型对象中
TaskSeriesCollection taskseriescollection = new TaskSeriesCollection();
taskseriescollection.add(taskseries);
taskseriescollection.add(taskseries1);
//建立JFreeChart图表对象,将taskseriescollection对象作为参数传入createGanttChart方法中
JFreeChart jfreechart = ChartFactory.createGanttChart("项目任务进度甘特图",
                  "项目任务",
                  "日期",
                  taskseriescollection,
                  true,
                  true,
                  false);
CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot();
categoryplot.getDomainAxis().setMaximumCategoryLabelWidthRatio(10F);
GanttRenderer ganttrenderer = (GanttRenderer) categoryplot.getRenderer();
//setDrawBarOutline(...)方法表示是否显示虚线,为false时表示不显示
ganttrenderer.setDrawBarOutline(false);

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 500, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="500" height="300" border="0" usemap="#<%= filename %>">

在该JSP页面中,用到了DateUtil类date方法,该方法的功能是通过年、月和日信息构建Date对象,该类的代码如下:

package gantt;

import java.util.Calendar;
import java.util.Date;

/**
 * 日期助手类.
 */
public class DateUtil {
    /**
      * 根据年月日构建Date对象.
      * @param year 年
      * @param month 月
      * @param day 日
      * @return 返回构建的date对象
      */
    public static Date date(int year, int month, int day) {
          Calendar calendar = Calendar.getInstance();
          calendar.set(year, month, day);
          Date date1 = calendar.getTime();
          return date1;
    }
}

在浏览器中输入地址:http://localhost:8080/jfreechart/gantt/gantt1.jsp,运行效果如图3-23所示。

图3-23 用甘特图显示项目任务

在某些情况下,可能需要通过甘特图来表示各任务的完成情况,这种甘特图在JFreeChart中如何实现呢?接着继续进行探讨。

首先,读者在WebRoot/gantt目录下建立gantt2.jsp文件。在上面的例子中,提供了创建任务的方法,其实在创建各个任务后,还可以调用该任务对象的setPercentComplete(double arg)方法,来设置各个任务的进度情况,0表示尚未进行,1表示任务已经完成,对于通过setPercentComplete(double arg)方法进行了任务设置的任务,在显示时,蓝色任务条中间默认将会出现一条绿线或红线,或者两种颜色混合的线,绿色表示的是已完成的部分,红色表示未完成的部分。gantt2.jsp的代码如下所示:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.servlet.ServletUtilities,
              gantt.DateUtil,
              java.awt.Color,
              org.jfree.chart.plot.CategoryPlot,
              org.jfree.chart.renderer.category.CategoryItemRenderer,
              org.jfree.data.gantt.*"%>


<%
//创建任务序列
TaskSeries taskseries =new TaskSeries("进度");

//创建和设置任务“编写计划”
Task task1 = new Task("编写计划", DateUtil.date(2007, 3, 1), DateUtil.date(2007, 3, 5));
task1.setPercentComplete(1.0D);

//创建和设置任务“需求获取”
Task task2 = new Task("需求获取", DateUtil.date(2007, 3, 8), DateUtil.date(2007, 3, 9));
task2.setPercentComplete(1.0D);

//创建任务“需求获取”,该任务包括两个子任务:第一阶段任务和第二阶段任务,并设置好子任务的完成情况
Task task3 = new Task("需求分析", DateUtil.date(2007, 3, 10), DateUtil.date(2007, 4, 5));
Task task31 = new Task("第一阶段任务", DateUtil.date(2007, 3, 10), DateUtil.date(2007, 3, 25));
Task task32 = new Task("第二阶段任务", DateUtil.date(2007, 4, 1), DateUtil.date(2007, 4, 5));
task31.setPercentComplete(1.0D);
task32.setPercentComplete(1.0D);
task3.addSubtask(task31);
task3.addSubtask(task31);

//创建任务“系统设计”,该任务包括三个子任务:架构设计、概要设计和详细设计,并设置好子任务的完成情况
//其中“架构设计”和“概要设计”为已完成状态,而详细设计才完成50%
Task task4 = new Task("系统设计", DateUtil.date(2007, 4, 6), DateUtil.date(2007, 4, 30));
Task task41 = new Task("架构设计", DateUtil.date(2007, 4, 6), DateUtil.date(2007, 4, 14));
Task task42 = new Task("概要设计", DateUtil.date(2007, 4, 15), DateUtil.date(2007, 4, 20));
Task task43 = new Task("详细设计", DateUtil.date(2007, 4, 21), DateUtil.date(2007, 4, 30));
task41.setPercentComplete(1.0D);
task42.setPercentComplete(1.0D);
task43.setPercentComplete(0.5D);
task4.addSubtask(task41);
task4.addSubtask(task42);
task4.addSubtask(task43);

//创建和设置任务“设计评审”
Task task5 = new Task("设计评审", DateUtil.date(2007, 5, 1), DateUtil.date(2007, 5, 4));
task5.setPercentComplete(0.7D);

//创建和设置任务“系统实现”
Task task6 = new Task("系统实现", DateUtil.date(2007, 5, 5), DateUtil.date(2007, 7, 15));
task6.setPercentComplete(0.59999999999999998D);

//创建和设置任务“测试”
Task task7 = new Task("测试", DateUtil.date(2007, 7, 16), DateUtil.date(2007, 9, 31));
task7.setPercentComplete(0.0D);

//将各任务加入到任务序列中
taskseries.add(task1);
taskseries.add(task2);
taskseries.add(task3);
taskseries.add(task4);
taskseries.add(task5);
taskseries.add(task6);
taskseries.add(task7);

//创建任务序列集合,并将前面创建的任务序列加入到集合中
TaskSeriesCollection taskseriescollection = new TaskSeriesCollection();
taskseriescollection.add(taskseries);
//传入taskseriescollection到createGanttChart方法中,创建JFreeChart对象
JFreeChart jfreechart = ChartFactory.createGanttChart("项目完成情况甘特图", "任务", "日期",
                  taskseriescollection,  true, true, false);
CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot();
categoryplot.getDomainAxis().setMaximumCategoryLabelWidthRatio(10F);
CategoryItemRenderer categoryitemrenderer = categoryplot.getRenderer();
//设置任务序列的颜色为蓝色(默认为红色)
categoryitemrenderer.setSeriesPaint(0, Color.blue);
String filename = ServletUtilities.saveChartAsPNG(jfreechart, 700, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="700" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/gantt/gantt2.jsp,运行效果如图3-24所示。

图3-24 用甘特图表示项目任务的完成情况示例

3.4.5 区域图

区域图一般用来表示某种变化的区间性,在项目开发过程中相对来说使用较少。在本小节中读者将通过开发几个区域图的例子,来进一步熟悉和掌握在JFreeChart中区域图的使用。

首先,读者在工程的WebRoot下创建area目录,该目录用来存放区域图的各例子。接着在WebRoot/area目录下新建area1.jsp文件,该例向读者展示区域图表的简单使用。创建区域图的JFreeChart对象的方法是org.jfree.chart.ChartFactory类的createAreaChart(…)方法。area1.jsp的代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.servlet.ServletUtilities,
              java.awt.*,
              org.jfree.chart.axis.*,
              org.jfree.chart.plot.CategoryPlot,
              org.jfree.chart.plot.PlotOrientation,
              org.jfree.chart.title.TextTitle,
              org.jfree.data.category.DefaultCategoryDataset,
              org.jfree.ui.*,
              org.jfree.util.UnitType"%>

<%
//设置数据集
DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
defaultcategorydataset.addValue(1.0D, "序列1", "种类1");
defaultcategorydataset.addValue(4D, "序列1", "种类2");
defaultcategorydataset.addValue(3D, "序列1", "种类3");
defaultcategorydataset.addValue(5D, "序列1", "种类4");
defaultcategorydataset.addValue(5D, "序列1", "种类5");
defaultcategorydataset.addValue(7D, "序列1", "种类6");
defaultcategorydataset.addValue(7D, "序列1", "种类7");
defaultcategorydataset.addValue(8D, "序列1", "种类8");
defaultcategorydataset.addValue(5D, "序列2", "种类1");
defaultcategorydataset.addValue(7D, "序列2", "种类2");
defaultcategorydataset.addValue(6D, "序列2", "种类3");
defaultcategorydataset.addValue(8D, "序列2", "种类4");
defaultcategorydataset.addValue(4D, "序列2", "种类5");
defaultcategorydataset.addValue(4D, "序列2", "种类6");
defaultcategorydataset.addValue(2D, "序列2", "种类7");
defaultcategorydataset.addValue(1.0D, "序列2", "种类8");
defaultcategorydataset.addValue(4D, "序列3", "种类1");
defaultcategorydataset.addValue(3D, "序列3", "种类2");
defaultcategorydataset.addValue(2D, "序列3", "种类3");
defaultcategorydataset.addValue(3D, "序列3", "种类4");
defaultcategorydataset.addValue(6D, "序列3", "种类5");
defaultcategorydataset.addValue(3D, "序列3", "种类6");
defaultcategorydataset.addValue(4D, "序列3", "种类7");
defaultcategorydataset.addValue(3D, "序列3", "种类8");

//创建区域图的JFreeChart对象.
//PlotOrientation.VERTICAL表示将其设置垂直状态的.
JFreeChart jfreechart = ChartFactory.createAreaChart("区域图示例", "种类", "数值",
                      defaultcategorydataset, PlotOrientation.VERTICAL, true, true, false);

//将底色设置为白色(默认为灰色)
jfreechart.setBackgroundPaint(Color.white);

//设置子标题
TextTitle texttitle = new TextTitle("该例展示了JFreeChart的区域图的使用,我们使用子标题来展示当有一个很长的标题
或者子标题时可能会发生什么情况.");
texttitle.setFont(new Font("SansSerif", 0, 12));
texttitle.setPosition(RectangleEdge.TOP);
texttitle.setPadding(new RectangleInsets(UnitType.RELATIVE, 0.05D, 0.05D, 0.05D, 0.05D));
texttitle.setVerticalAlignment(VerticalAlignment.BOTTOM);
jfreechart.addSubtitle(texttitle);

CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot();
//设置透明度为半透明,当为1时表示不透明.
categoryplot.setForegroundAlpha(0.5F);
categoryplot.setAxisOffset(new RectangleInsets(5D, 5D, 5D, 5D));
//设置图片的背景色为亮灰色
categoryplot.setBackgroundPaint(Color.lightGray);
//设置图片后的竖虚线为可见状态
categoryplot.setDomainGridlinesVisible(true);
//设置图片后的竖虚线的颜色为白色(默认为亮灰色)
categoryplot.setDomainGridlinePaint(Color.white);
//设置图片后的横虚线为可见状态
categoryplot.setRangeGridlinesVisible(true);
//设置图片后的横虚线的颜色为白色(默认为亮灰色)
categoryplot.setRangeGridlinePaint(Color.white);
CategoryAxis categoryaxis = categoryplot.getDomainAxis();
categoryaxis.setCategoryLabelPositions(CategoryLabelPositions.UP 45);
categoryaxis.setLowerMargin(0.0D);
categoryaxis.setUpperMargin(0.0D);
categoryaxis.addCategoryLabelToolTip("种类1", "第一个种类.");
categoryaxis.addCategoryLabelToolTip("种类2", "第二个种类.");
categoryaxis.addCategoryLabelToolTip("种类3", "第三个种类");
NumberAxis numberaxis = (NumberAxis) categoryplot.getRangeAxis();
numberaxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
numberaxis.setLabelAngle(0.0D);

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 600, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="600" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/area/area1.jsp,运行效果如图3-25所示。

图3-25 区域图示例

上例创建的数据集是DefaultCategoryDataset型的,读者也可以通过XYDataset的数据集来构造区域图,接着在WebRoot/area目录下创建文件area2.jsp。该jsp中的JFreeChart对象是通过org.jfree.chart.ChartFactory的createXYAreaChart(…)方法来创建的,该页面的代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="org.jfree.chart.ChartFactory,
              org.jfree.chart.JFreeChart,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.chart.axis.DateAxis,
              org.jfree.chart.plot.PlotOrientation,
              org.jfree.chart.plot.XYPlot,
              org.jfree.data.time.*"%>


<%
//构造时序序列
TimeSeries timeseries = new TimeSeries("随机数时序序列");
double d = 0.0D;
Day day = new Day();
for (int i = 0; i < 200; i++) {
    d = (d + Math.random()) - 0.5D;
    timeseries.add(day, d);
    day = (Day)day.next();
}
//构造XYDataset数据集(说明:TimeSeriesCollection实现了XYDataset类)
TimeSeriesCollection xydataset = new TimeSeriesCollection(timeseries);
JFreeChart jfreechart = ChartFactory.createXYAreaChart( "XY区域图", "时间", "数值",
                  xydataset, PlotOrientation.VERTICAL, true, true, false);
XYPlot xyplot = (XYPlot)jfreechart.getPlot();
//设置X轴参数
DateAxis dateaxis = new DateAxis("时间");
dateaxis.setLowerMargin(0.0D);
dateaxis.setUpperMargin(0.0D);
xyplot.setDomainAxis(dateaxis);
//设置为半透明
xyplot.setForegroundAlpha(0.5F);
String filename = ServletUtilities.saveChartAsPNG(jfreechart, 600, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="600" height="400" border="0" usemap="#<%= filename %>">

从代码可以看出,区域图的数据是随机生成的,因此图形是动态变化的,在浏览器中输入地址:http://localhost:8080/jfreechart/area/area2.jsp,运行效果如图3-26所示。

图3-26 XY区域图示例

3.4.6 曲线图

在JFreeChart中,绘制曲线图的功能非常强大,例如绘制波形图、阶梯图,以及其他一些无规则的统计图形等。

首先,读者在工程的WebRoot目录下建立line目录,用来存放本小节的各实例。在第一个例子中,读者将学习如何绘制波形图,该类型的JFreeChart对象的生成采用org.jfree.chart.ChartFactory类的createXYLineChart(…)方法。读者在WebRoot/line目录下建立名为line1的jsp文件,波形图的数据集的生成放在一个自定义的类SampleXYDataset中,该类实现类XYDataset,读者可以在src目录下建立line目录,在该目录下建立SampleXYDataset.java文件,该类的代码如下:

package line;
import org.jfree.data.general.DatasetChangeEvent;
import org.jfree.data.xy.AbstractXYDataset;
import org.jfree.data.xy.XYDataset;
/**
 * XYDataset接口的实现类,该类获得一个正弦或余弦的数据集.
 */
public class SampleXYDataset extends AbstractXYDataset implements XYDataset {
    private double translate;

    public SampleXYDataset() {
          translate = 0.0D;
    }

    public double getTranslate() {
          return translate;
    }

    public void setTranslate(double d) {
          translate = d;
          notifyListeners(new DatasetChangeEvent(this, this));
    }

    public Number getX(int i, int j) {
          return new Double(-10D + translate + (double) j / 10D);
    }

    public Number getY(int i, int j) {
          if (i == 0)
              return new Double(Math.cos(-10D + translate + (double) j / 10D));
          else
              return new Double(2D * Math
                      .sin(-10D + translate + (double) j / 10D));
    }

    public int getSeriesCount() {
          return 2;
    }

    public Comparable getSeriesKey(int i) {
          if (i == 0)
              return "y = cosine(x)";
          if (i == 1)
              return "y = 2*sine(x)";
          else
              return "Error";
    }

    public int getItemCount(int i) {
        return 200;
    }
}

接着在line.jsp中调用该类生成波形图的数据集,并通过org.jfree.chart.ChartFactory类的createXYLineChart(…)方法来创建曲线图,jsp页面的代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import = "org.jfree.chart.ChartFactory,
                  org.jfree.chart.JFreeChart,
                  org.jfree.chart.servlet.ServletUtilities,
                  line.SampleXYDataset,
                  org.jfree.chart.plot.PlotOrientation,
                  org.jfree.chart.plot.XYPlot,
                  org.jfree.chart.renderer.xy.XYLineAndShapeRenderer,
                  org.jfree.data.xy.XYDataset"%>
<%
XYDataset xydataset = new SampleXYDataset();
JFreeChart jfreechart = ChartFactory.createXYLineChart("曲线图的例子——波形图", "X", "Y",
                      xydataset, PlotOrientation.VERTICAL, true, true, false);
XYPlot xyplot = (XYPlot)jfreechart.getPlot();
xyplot.getDomainAxis().setLowerMargin(0.0D);
xyplot.getDomainAxis().setUpperMargin(0.0D);
XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer)xyplot.getRenderer();
xylineandshaperenderer.setLegendLine(new java.awt.geom.Rectangle2D.Double(-4D, -3D, 8D, 6D));

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 600, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="600" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/line/line1.jsp,运行效果如图3-27所示。

图3-27 曲线图的示例——波形图

波形图是一种常用的曲线图,而阶梯图也是一种常用的曲线图,在下面的例子中,读者将学习如何制作阶梯图。

首先在WebRoot/line目录下创建line2.jsp文件,该例的JFreeChart对象的生成与前例一样,是采用org.jfree.chart.ChartFactory的createXYLineChart(…)方法,不过数据集的生成与前例有少许不同。该例的代码如下:

<%@ page contentType="text/html;charset=GBK"%>
<%@ page import = "org.jfree.chart.ChartFactory,
                  org.jfree.chart.JFreeChart,
                  org.jfree.chart.servlet.ServletUtilities,
                  java.awt.BasicStroke,
                  org.jfree.chart.labels.StandardXYToolTipGenerator,
                  org.jfree.chart.plot.PlotOrientation,
                  org.jfree.chart.plot.XYPlot,
                  org.jfree.chart.renderer.xy.XYStepRenderer,
                  org.jfree.data.xy.*"%>


<%
//序列1的创建及其数据
XYSeries xyseries = new XYSeries("序列1");
xyseries.add(1.0D, 3D);
xyseries.add(2D, 4D);
xyseries.add(3D, 2D);
xyseries.add(6D, 3D);

//序列2的创建及其数据
XYSeries xyseries1 = new XYSeries("序列2");
xyseries1.add(1.0D, 7D);
xyseries1.add(2D, 6D);
xyseries1.add(3D, 9D);
xyseries1.add(4D, 5D);
xyseries1.add(6D, 4D);
XYSeriesCollection xydataset = new XYSeriesCollection();
xydataset.addSeries(xyseries);
xydataset.addSeries(xyseries1);

//通过createXYLineChart(...)创建JFreeChart曲线图对象
JFreeChart jfreechart = ChartFactory.createXYLineChart("阶梯图实例1", "X", "Y",
                      xydataset,  PlotOrientation.VERTICAL, true, true, false);
XYPlot xyplot = (XYPlot) jfreechart.getPlot();
XYStepRenderer xysteprenderer =new XYStepRenderer();
xysteprenderer.setBaseShapesVisible(true);
xysteprenderer.setSeriesStroke(0, new BasicStroke(2.0F));
xysteprenderer.setSeriesStroke(1, new BasicStroke(2.0F));
xysteprenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
xysteprenderer.setDefaultEntityRadius(6);
xyplot.setRenderer(xysteprenderer);

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 600, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="600" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/line/line2.jsp,运行效果如图3-28所示。

图3-28 曲线图的示例——阶梯图

读者可以看到,在上图中是未显示各点的数值的,若想要在阶梯图中显示各点的数值,还需要加入部分内容,读者可以在上例的代码xyplot.setRenderer(xysteprenderer);上加上如下代码:

//设置显示数字
xysteprenderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator());
xysteprenderer.setBaseItemLabelsVisible(true);
xysteprenderer.setBaseItemLabelFont(new Font("Dialog", 1, 14));

//设置显示的数字的位置
ValueAxis valueaxis = xyplot.getRangeAxis();
valueaxis.setUpperMargin(0.15D);

该例的运行效果如图3-29所示。

图3-29 带有具体数值的阶梯图的示例

3.4.7 组合图

Category Plot和XY Plot主要用来操作两个坐标轴(默认),分别叫Domain轴和Range轴。这些专用名词是建立这些地图可视化地把domain轴的值映射到value轴上的概念之上。而在效果上, Domain轴作为X轴,Range作为Y轴。JFreeChart的一个更强大的功能是同一个Chart可以提供多个domain轴和多个value轴,同时轴类是可克隆和可串行化的。

JFreeChart支持组合图形,可以通过使用一个整体图形来管理相同坐标的图形,从而可以使不同的图形在同一个坐标体系内得到比较,达到统计直观对比的目的。它主要可以组合以下几大类型的图形:同Domain轴组合图(Category Plot)、同Range轴组合图(Range Category)、同X轴组合图(Domain XY Plot)、同Y轴组合图(Range XY Plot)。在这一小节中将介绍使用JFreeChart提供的这一方面实用功能来演示如何在实际需求中应用。主要介绍同Domain轴组合图和同Range轴组合图,其他两种具有相似性,就不一一列举,请读者参考此例自行阅读。

1. 同Domain轴组合图(Category Plot)

此功能主要提供在同一个分析领域,可以抽取或图像化不同值的图形,它们可以共用或共享一个相同的domain轴(简单点就可理解为X轴),而每一个图形却拥有自己的range轴(简单点可以理解为Y轴)。当然可以按水平或垂直的方向进行图形的显示,在本例中使用水平方向显示图形。

首先编写后台java类:Category_Plot_Demo.java,其代码如下:

package com.jfreechart;


import 略


public class Category Plot Demo extends ApplicationFrame {


    String s2 = "伍英";
    String s3 = "李得";
    String s4 = "王冠";
    String s5 = "陈砂";
    String s6 = "刘行";
    String s7 = "谢放";
    String s8 = "李实";
    String s9 = "邓平";


    public Category Plot Demo(String s) {
        super(s);
    }
    public  CategoryDataset createDataset1() {
        DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
        String s = "家庭来源";
        String s1 = "学校补助";
        defaultcategorydataset.addValue(2000.0D, s, s2);
        defaultcategorydataset.addValue(4000D, s, s3);
        defaultcategorydataset.addValue(3000D, s, s4);
        defaultcategorydataset.addValue(5000D, s, s5);
        defaultcategorydataset.addValue(5000D, s, s6);
        defaultcategorydataset.addValue(7000D, s, s7);
        defaultcategorydataset.addValue(7000D, s, s8);
        defaultcategorydataset.addValue(8000D, s, s9);
        defaultcategorydataset.addValue(5000D, s1, s2);
        defaultcategorydataset.addValue(7000D, s1, s3);
        defaultcategorydataset.addValue(6000D, s1, s4);
        defaultcategorydataset.addValue(8000D, s1, s5);
        defaultcategorydataset.addValue(4000D, s1, s6);
        defaultcategorydataset.addValue(4000D, s1, s7);
        defaultcategorydataset.addValue(2000D, s1, s8);
        defaultcategorydataset.addValue(2000.0D, s1, s9);
        return defaultcategorydataset;
    }

    public  CategoryDataset createDataset2() {
        DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
        String s = "学杂费";
        String s1 = "生活费";

        defaultcategorydataset.addValue(3000D, s, s2);
        defaultcategorydataset.addValue(4000D, s, s3);
        defaultcategorydataset.addValue(6000D, s, s4);
        defaultcategorydataset.addValue(5000D, s, s5);
        defaultcategorydataset.addValue(5000D, s, s6);
        defaultcategorydataset.addValue(7000D, s, s7);
        defaultcategorydataset.addValue(7000D, s, s8);
        defaultcategorydataset.addValue(8000D, s, s9);
        defaultcategorydataset.addValue(8000D, s1, s2);
        defaultcategorydataset.addValue(7000D, s1, s3);
        defaultcategorydataset.addValue(6000D, s1, s4);
        defaultcategorydataset.addValue(8000D, s1, s5);
        defaultcategorydataset.addValue(7000D, s1, s6);
        defaultcategorydataset.addValue(5000D, s1, s7);
        defaultcategorydataset.addValue(4000D, s1, s8);
        defaultcategorydataset.addValue(3000D, s1, s9);
        return defaultcategorydataset;
    }

    private  JFreeChart createChart() {
        //绘制第一张对比图
        CategoryDataset categorydataset = createDataset1();
        NumberAxis numberaxis = new NumberAxis("年经济来源");
        numberaxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        LineAndShapeRenderer lineandshaperenderer = new LineAndShapeRenderer();
        lineandshaperenderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator());
        CategoryPlot categoryplot = new CategoryPlot(categorydataset, null,
    numberaxis, lineandshaperenderer);
        categoryplot.setDomainGridlinesVisible(true);
        CategoryDataset categorydataset1 = createDataset2();

        //绘制第二张对比图
        NumberAxis numberaxis1 = new NumberAxis("年生活支出");
        numberaxis1.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        BarRenderer barrenderer = new BarRenderer();
        barrenderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator());
        CategoryPlot categoryplot1 = new CategoryPlot(categorydataset1, null,numberaxis1, barrenderer);
        categoryplot1.setDomainGridlinesVisible(true);
        CategoryAxis categoryaxis = new CategoryAxis("学生");
        CombinedDomainCategoryPlot combineddomaincategoryplot =
    new CombinedDomainCategoryPlot(categoryaxis);
        combineddomaincategoryplot.add(categoryplot, 2);
        combineddomaincategoryplot.add(categoryplot1, 2);
        JFreeChart jfreechart = new JFreeChart("某班级学生生活收支对比图",
    new Font("SansSerif", 1,12), combineddomaincategoryplot, true);
        return jfreechart;
    }

    public  void JfreeChart(HttpServletRequest request,HttpServletResponse response) throws IOException {
        Writer out = response.getWriter();
        HttpSession session = request.getSession();

        //获得图形对象
        JFreeChart chart = createChart();

        // 把生成的图片放到临时目录
        StandardEntityCollection sec = new StandardEntityCollection();
        ChartRenderingInfo info = new ChartRenderingInfo(sec);

        // 500是图片长度,300是图片高度,session为HttpSession对象
        String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,info, session);

        //输出图片
        PrintWriter pw = new PrintWriter(out);
        ChartUtilities.writeImageMap(pw, "map0", info, false);
        String graphURL = request.getContextPath()
                  + "/DisplayChart?filename=" + filename;
        request.setAttribute("graphURL", graphURL);
    }
}

此时可以在WebRoot/ combined目录下建立另一个名为Category_Plot_Demo.jsp的文件,在Category_Plot_Demo.jsp页面上进行调用上面Category_Plot_Demo.java中所生成的图片,从而显示图片。Category_Plot_Demo.jsp的代码如下:

<%@ page import="com.jfreechart.Category Plot Demo"%>
<%@ page contentType="text/html;charset=GBK"%>
<html>
  <head>
<title>同Domain轴组合图示例</title>

    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>

  <body>
    <%
      Category Plot Demo category Plot Demo=new Category Plot Demo("");
      category Plot Demo.JfreeChart(request,response);
      String graphURL=(String)request.getAttribute("graphURL");
    %>
    <img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#map0">
  </body>
</html>

在浏览器中输入如下地址:http://localhost:8080/jfreechart/combined/Category_Plot_Demo.jsp,运行效果如图3-30所示。

图3-30 同Domain轴组合图示例

2. 同Range轴组合图(Range Category)

此功能主要提供在同一个范围值,可以抽取或图像化不同值的图形,它们可以共用或共享一个相同的range轴(简单点就可理解为Y轴),而每一个图形却拥有自己的domain轴(简单点可以理解为X轴)。当然可以按水平或垂直的方向进行图形的显示,在本例中使用水平方向显示图形。

首先编写后台Java类:Range_Plot_Demo.java,其代码如下:

package com.jfreechart;
import 略

public class Range Plot Demo extends ApplicationFrame {

    public Range Plot Demo(String s) {
          super(s);
    }

    public static CategoryDataset createDataset1() {
        DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
        String s = "学杂费";
        String s1 = "生活费";
        String s2 = "伍英";
        String s3 = "李得";
        String s4 = "王冠";
        String s5 = "陈砂";
        String s6 = "刘行";
        String s7 = "谢放";
        String s8 = "李实";
        String s9 = "邓平";
        defaultcategorydataset.addValue(2300D, s, s2);
        defaultcategorydataset.addValue(4000D, s, s3);
        defaultcategorydataset.addValue(3000D, s, s4);
        defaultcategorydataset.addValue(5000D, s, s5);
        defaultcategorydataset.addValue(5000D, s, s6);
        defaultcategorydataset.addValue(7000D, s, s7);
        defaultcategorydataset.addValue(7000D, s, s8);
        defaultcategorydataset.addValue(8000D, s, s9);
        defaultcategorydataset.addValue(5000D, s1, s2);
        defaultcategorydataset.addValue(7000D, s1, s3);
        defaultcategorydataset.addValue(6000D, s1, s4);
        defaultcategorydataset.addValue(8000D, s1, s5);
        defaultcategorydataset.addValue(4000D, s1, s6);
        defaultcategorydataset.addValue(4000D, s1, s7);
        defaultcategorydataset.addValue(3000D, s1, s8);
        defaultcategorydataset.addValue(2000D, s1, s9);
        return defaultcategorydataset;
    }

    public static CategoryDataset createDataset2() {
        DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
        String s = "家庭来源";
        String s1 = "学校补助";
        String s2 = "北京";
        String s3 = "山东";
        String s4 = "吉林";
        String s5 = "天津";
        defaultcategorydataset.addValue(3000D, s, s2);
        defaultcategorydataset.addValue(4000D, s, s3);
        defaultcategorydataset.addValue(3000D, s, s4);
        defaultcategorydataset.addValue(5000D, s, s5);
        defaultcategorydataset.addValue(5000D, s1, s2);
        defaultcategorydataset.addValue(7000D, s1, s3);
        defaultcategorydataset.addValue(6000D, s1, s4);
        defaultcategorydataset.addValue(5000D, s1, s5);
        return defaultcategorydataset;
    }

    private static JFreeChart createChart() {
        //创建第一幅图
        CategoryDataset categorydataset = createDataset1();
        CategoryAxis categoryaxis = new CategoryAxis("广州学生");
        categoryaxis.setCategoryLabelPositions(CategoryLabelPositions.UP 45);
        categoryaxis.setMaximumCategoryLabelWidthRatio(5F);
        LineAndShapeRenderer lineandshaperenderer = new LineAndShapeRenderer();
        lineandshaperenderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator());
        CategoryPlot categoryplot = new CategoryPlot(categorydataset, categoryaxis,
                                                                        null, lineandshaperenderer);
        categoryplot.setDomainGridlinesVisible(true);
        //创建第二幅图
        CategoryDataset categorydataset1 = createDataset2();
        CategoryAxis categoryaxis1 = new CategoryAxis("北方学生");
        categoryaxis1.setCategoryLabelPositions(CategoryLabelPositions.UP 45);
        categoryaxis1.setMaximumCategoryLabelWidthRatio(5F);
        BarRenderer barrenderer = new BarRenderer();
        barrenderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator());
        CategoryPlot categoryplot1 = new CategoryPlot(categorydataset1, categoryaxis1, null, barrenderer);
        categoryplot1.setDomainGridlinesVisible(true);
        NumberAxis numberaxis = new NumberAxis("年生活消费(元)");
        CombinedRangeCategoryPlot combinedrangecategoryplot =
    new CombinedRangeCategoryPlot(numberaxis);
        combinedrangecategoryplot.add(categoryplot, 3);
        combinedrangecategoryplot.add(categoryplot1, 2);
        //设置图形水平或是垂直显示
        combinedrangecategoryplot.setOrientation(PlotOrientation.VERTICAL );
        JFreeChart jfreechart = new JFreeChart("学生生活收支对比图",
    new Font("SansSerif", 1, 12), combinedrangecategoryplot, true);
        return jfreechart;
    }
    public  void JfreeChart(HttpServletRequest request,HttpServletResponse response) throws IOException {
        Writer out = response.getWriter();
        HttpSession session = request.getSession();

        //获得图形对象
        JFreeChart chart = createChart();

        // 把生成的图片放到临时目录
        StandardEntityCollection sec = new StandardEntityCollection();
        ChartRenderingInfo info = new ChartRenderingInfo(sec);

        // 500是图片长度,300是图片高度,session为HttpSession对象
        String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,info, session);

        //输出图片
        PrintWriter pw = new PrintWriter(out);
        ChartUtilities.writeImageMap(pw, "map0", info, false);

        String graphURL = request.getContextPath()
                  + "/DisplayChart?filename=" + filename;
        request.setAttribute("graphURL", graphURL);
    }
}

此时,可以在WebRoot/ combined目录下建立Range_Plot_Demo.jsp文件,在Range_Plot_Demo.jsp页面上进行调用上面Range_Plot_Demo.java中所生成的图片,从而显示图片。Range_Plot_Demo.jsp的代码如下:

<%@ page import="com.jfreechart.Range Plot Demo"%>
<%@ page contentType="text/html;charset=GBK"%>
<html>
  <head>
    <title>同Range轴组合图示例</title>
  </head>

  <body>
    <%
      Range Plot Demo range Plot Demo=new Range Plot Demo("");
      range Plot Demo.JfreeChart(request,response);
      String graphURL=(String)request.getAttribute("graphURL");
    %>
    <img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#map0">
  </body>
</html>

在浏览器中输入如下地址:http://localhost:8080/jfreechart/combined/Range_Plot_Demo.jsp,运行效果如图3-31所示。

图3-31 同Range轴的组合图

3.4.8 通过JDBC填充Datasets

JDBC提供了与关系型数据库进行交互的较上层的API。同时,可以充分地体现Java的跨平台性,可以使Java代码在不同的平台下运行。JDBC驱动对绝大多数数据库都提供了驱动。

在JFreeChart中,提供了可以直接通过JDBC从数据库中提取数据填充Dataset的功能。这样的datasets包括以下几类:

● 饼图(JDBCPieDataset):提供饼图的dataset获取方法;

● 柱状图(JDBCCategoryDataset):提供种类图的dataset获取方法;

● 曲线图(JDBCXYDataset):提供XY坐标系图的dataset获取方法。

首先来看JDBCPieDataset,如何从数据库直接读取数据填充dataset,从而更加方便地显示图形。

1. 饼图(JDBCPieDataset)

(1)创建数据库

这里为了方便起见,数据库采用MySQL,下载地址为:http://download.mysql.cn/。同时,为了使用JDBC,需要下载JDBC驱动,下载地址为:http://dev.mysql.com/downloads/connector/j/5.0.html

请将下载完的MySQL JDBC驱动复制到项目的lib下,如本例所示:jfreechart\WebRoot\WEB-INF\lib\mysql-connector-java-5.0.4-bin.jar。

创建数据库:语句create database jfreechartdb。

导入数据:如果采用了MySQL前端工具的话,可以直接导入src\com\jfreechart\JDBC_Pie_Demo.sql文件。如果没有前端工具话,可以直接采用如下语句来直接创建:

DROP TABLE IF EXISTS `piedata1`;
CREATE TABLE `piedata1` (
  `category` varchar(32) default NULL,
  `value` float default NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `piedata1` VALUES ('Eclipse',53.4);
INSERT INTO `piedata1` VALUES ('NetBean',43.4);
INSERT INTO `piedata1` VALUES ('JBuilder',17.4);
INSERT INTO `piedata1` VALUES ('other',7.4);

(2)编写Java类

有了数据库后,就可以开始编写Java类:JDBC_Pie_Demo.java,此类位于src/com/jfreechart/JDBC_Pie_Demo.java,其代码如下:

package com.jfreechart;
import 略

public class JDBC Pie Demo extends ApplicationFrame {

    public JDBC Pie Demo(String s) {
          super(s);
    }
    private PieDataset readData() {
          JDBCPieDataset jdbcpiedataset = null;
          // 设置数据库地址
          String s = "jdbc:mysql://localhost/jfreechartdb";
          try {
              // 设置JDBC驱动
              Class.forName("com.mysql.jdbc.Driver");
          } catch (ClassNotFoundException classnotfoundexception) {
              System.err.print("找不到相应的class文件: ");
              System.err.println(classnotfoundexception.getMessage());
          }
          try {
              // 建立连接
              Connection connection = DriverManager.getConnection(s,
                      "root", // 用户
                      "root" // 密码
            );
            //使用已经建立好的连接创建新的JDBCPieDataset
            jdbcpiedataset = new JDBCPieDataset(connection);
            String s1 = "SELECT * FROM PIEDATA1;";
            jdbcpiedataset.executeQuery(s1);
            connection.close();
        } catch (SQLException sqlexception) {
            System.err.print("SQL错误: ");
            System.err.println(sqlexception.getMessage());
        } catch (Exception exception) {
            System.err.print("错误: ");
            System.err.println(exception.getMessage());
        }
        return jdbcpiedataset;
    }

    public  void JfreeChart(HttpServletRequest request,HttpServletResponse response) throws IOException {
        Writer out = response.getWriter();
        HttpSession session = request.getSession();
        //获得图形对象
        JFreeChart chart = createChart();
        // 把生成的图片放到临时目录
        StandardEntityCollection sec = new StandardEntityCollection();
        ChartRenderingInfo info = new ChartRenderingInfo(sec);
        // 500是图片长度,300是图片高度,session为HttpSession对象
        String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,info, session);
        //输出图片
        PrintWriter pw = new PrintWriter(out);
        ChartUtilities.writeImageMap(pw, "map0", info, false);

        String graphURL = request.getContextPath()
                  + "/DisplayChart?filename=" + filename;
        request.setAttribute("graphURL", graphURL);
    }

    private  JFreeChart createChart() {
        //PieDataset通过学习JDBC从数据库中读取数据,返回的数据格式要求有两列,
        //第一列包含VARCHAR的数据,第二列包含数字型数据
        PieDataset piedataset = readData();
        JFreeChart jfreechart =
                          ChartFactory.createPieChart("Java IDE使用率分布图",piedataset, true, true, false);
        jfreechart.setBackgroundPaint(Color.yellow);
        PiePlot pieplot = (PiePlot) jfreechart.getPlot();
        pieplot.setNoDataMessage("无可显示的数据");
        return jfreechart;
    }
}

(3)编写JSP页面

此时,可以在WebRoot/ jdbc目录下建立另一个名为JDBC_Pie_Demo.jsp的文件,在JDBC_Pie_Demo.jsp页面上调用上面JDBC_Pie_Demo.java中所生成的图片,从而显示图片。JDBC_Pie_Demo.jsp代码如下所示:

<%@ page import="com.jfreechart.JDBC Pie Demo"%>
<%@ page contentType="text/html;charset=GBK"%>
<html>
  <head>
    <title>采用JDBC填充Dataset绘制饼图</title>
  </head>

  <body>
    <%
      JDBC Pie Demo jdbc Pie Demo=new JDBC Pie Demo("");
      jdbc Pie Demo.JfreeChart(request,response);
      String graphURL=(String)request.getAttribute("graphURL");
    %>
    <img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#map0">
  </body>
</html>

在浏览器中输入地址:http://localhost:8080/jfreechart/jdbc/JDBC_Pie_Demo.jsp,运行效果如图3-32所示。

图3-32 通过JDBC填充Datasets示例(一)

2.柱状图(JDBCCategoryDataset)

(1)创建数据库

导入数据:如果采用了MySQL前端工具的话,可以直接导入src\com\jfreechart\JDBC_Category_Demo.sql文件。如果没有前端工具,可以直接采用如下语句来直接创建:

DROP TABLE IF EXISTS `categorydata1`;
CREATE TABLE 'categorydata1` (
  `category` varchar(32) default NULL,
  `2005` float default NULL,
  `2006` float default NULL,
  `2007` float default NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `categorydata1` VALUES ('Eclipse',54.3,32.1,23.4);
INSERT INTO `categorydata1` VALUES ('NetBean',33.4,54.3,75.2);
INSERT INTO `categorydata1` VALUES ('JBuilder',17.9,24.8,17.1);
INSERT INTO `categorydata1` VALUES ('other',7.9,10.8,7.1);

(2)编写Java类

有了数据库后,就可以开始编写Java类:JDBC_Category_Demo.java,此类位于src/com/jfreechart/JDBC_Category_Demo.java,其代码如下所示:

package com.jfreechart;

import 略

public class JDBC Category Demo extends ApplicationFrame {
    public JDBC Category Demo(String s) {
          super(s);
    }

    private CategoryDataset readData() {
          JDBCCategoryDataset jdbccategorydataset = null;
          // 设置数据库地址
          String s = "jdbc:mysql://localhost/jfreechartdb";
          try {
              //设置JDBC驱动
              Class.forName("com.mysql.jdbc.Driver");
          }catch (ClassNotFoundException classnotfoundexception){
              System.err.print("找不到相应的class文件: ");
              System.err.println(classnotfoundexception.getMessage());
          }
          try {
              // 建立连接
              Connection connection = DriverManager.getConnection(s,
                        "root", // 用户
                        "root" // 密码
              );
              //使用已经建立好的连接创建新的JDBCCategoryDataset
              jdbccategorydataset = new JDBCCategoryDataset(connection);
              String s1 = "SELECT * FROM CATEGORYDATA1;";
              System.out.println("第一次...");
              jdbccategorydataset.executeQuery(s1);
              System.out.println("第二次...");
              jdbccategorydataset.executeQuery(s1);
              System.out.println("完成.");
              connection.close();
          } catch (SQLException sqlexception) {
              System.err.print("SQL错误: ");
              System.err.println(sqlexception.getMessage());
          } catch (Exception exception) {
              System.err.print("错误: ");
              System.err.println(exception.getMessage());
          }
          return jdbccategorydataset;
    }

    public  void JfreeChart(HttpServletRequest request,HttpServletResponse response) throws IOException {
          Writer out = response.getWriter();
          HttpSession session = request.getSession();
          //获得图形对象
          JFreeChart chart = createChart();
          // 把生成的图片放到临时目录
          StandardEntityCollection sec = new StandardEntityCollection();
          ChartRenderingInfo info = new ChartRenderingInfo(sec);
          // 500是图片长度,300是图片高度,session为HttpSession对象
          String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300,info, session);
          //输出图片
          PrintWriter pw = new PrintWriter(out);
          ChartUtilities.writeImageMap(pw, "map0", info, false);
          String graphURL = request.getContextPath()
                    + "/DisplayChart?filename=" + filename;
          request.setAttribute("graphURL", graphURL);
      }

      private  JFreeChart createChart(){
          CategoryDataset categorydataset = readData();
          JFreeChart jfreechart = ChartFactory.createBarChart3D("Java IDE各年份使用统计图", "Java IDE",
      "使用率(%)", categorydataset, PlotOrientation.VERTICAL, true, true, false);
          jfreechart.setBackgroundPaint(Color.yellow);
          return jfreechart;
      }
}

(3)编写JSP页面

此时,可以在WebRoot/jdbc目录下建立另一个名为JDBC_Category_Demo.jsp的文件,在JDBC_Category_Demo.jsp页面上调用上面JDBC_Category_Demo.java中所生成的图片,从而显示图片。JDBC_Category_Demo.jsp代码如下所示:

<%@ page import="com.jfreechart.JDBC Category Demo"%>
<%@ page contentType="text/html;charset=GBK"%>
<html>
  <head>
    <title>采用JDBC填充Dataset绘制柱状图</title>
  </head>

  <body>
    <%
      JDBC Category Demo jdbc Category Demo=new JDBC Category Demo("");
      jdbc Category Demo.JfreeChart(request,response);
      String graphURL=(String)request.getAttribute("graphURL");
    %>
    <img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#map0">
  </body>
</html>

在浏览器中输入如下地址:http://localhost:8080/jfreechart/jdbc/JDBC_ Category _Demo.jsp,运行效果如图3-33所示。

图3-33 通过JDBC填充Datasets示例(二)

3.4.9 仪表图

在JFreeChart中,还提供了绘制仪表图的功能,读者可以在JFreeChart中绘制米尺图、试管图等仪表图形,这在后续要讲到的jCharts图形开发技术中是没有的,首先,读者可以在WebRoot目录下建立meter目录,用来存放仪表图的例子。

首先从常用的仪表图着手,讲述米尺图的简单使用。在meter目录下建立meter1.jsp文件,该实例使用的数据集类为:org.jfree.data.general.DefaultValueDataset,图形类为:org.jfree.chart.plot.MeterPlot,其代码如下所示:

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="org.jfree.chart.JFreeChart,
              org.jfree.chart.servlet.ServletUtilities,
              java.awt.*,
              org.jfree.data.general.DefaultValueDataset,
              org.jfree.chart.plot.MeterPlot,
              org.jfree.chart.plot.MeterInterval,
              org.jfree.data.Range"%>


<%
//设置数据集,20为指针初始指向的位置
DefaultValueDataset dataset = new DefaultValueDataset(20);

//根据数据集设置米尺图形对象,并设置其属性
MeterPlot meterplot = new MeterPlot(dataset);
meterplot.addInterval(new MeterInterval("High", new Range(80D, 100D)));
meterplot.setDialOutlinePaint(Color.white);

//创建米尺图的JFreeChart对象
JFreeChart jfreechart = new JFreeChart(
                      "米尺图形",
                      JFreeChart.DEFAULT TITLE FONT,
                      meterplot,
                      false);

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 400, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="400" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/meter/meter1.jsp,运行效果如图3-34所示。

图3-34 米尺图形示例

【提示】

在上例中,米尺的指针是不该改变的,这不符合在实际开发过程中的需要,有时读者需要根据情况使米尺的指针能随实际情况改变,这时读者可以将JSP中的部分代码封装到Java文件中,在一定情况下改变图形的数据集中的值,从而能够改变图片。

3.4.10 多轴图

在前述的有轴图形中,都是只有一条X轴、一条Y轴,但有时有一些例外的情况,最常见的是有两条Y轴的情况,例如在一个由两个图组成的组合图中,两个图形的Y轴的刻度不一样时,需要用两条Y轴(左、右各一条)来表示不同的刻度信息。首先,读者在WebRoot目录下建立multipleAxis目录,用来存放本小节的多轴图,在接下来的例子中将展示一个2个Y轴的图形,该图形是一个由柱状图和曲线图组成的曲线图形。读者在WebRoot\multipleAxis目录建立dualAxis.jsp文件,在该文件中展示创建双轴图的用法。该文件的代码如下:

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import = "org.jfree.chart.ChartFactory,
                  org.jfree.chart.JFreeChart,
                  org.jfree.chart.servlet.ServletUtilities,
                  org.jfree.chart.plot.PlotOrientation,
                  java.awt.Color,
                  org.jfree.chart.axis.*,
                  org.jfree.chart.block.*,
                  org.jfree.chart.labels.StandardCategoryToolTipGenerator,
                  org.jfree.chart.plot.*,
                  org.jfree.chart.renderer.category.LineAndShapeRenderer,
                  org.jfree.chart.title.CompositeTitle,
                  org.jfree.chart.title.LegendTitle,
                  org.jfree.data.category.CategoryDataset,
                  org.jfree.data.category.DefaultCategoryDataset,
                  org.jfree.ui.*"%>

<%
//构造数据集1
String s = "全职收入";
String s1 = "兼职收入";

String s3 = "张三";
String s4 = "李四";
String s5 = "王五";
String s6 = "刘六";
String s7 = "谢七";
String s8 = "朱八";
String s9 = "陈九";
String s10 = "周十";
DefaultCategoryDataset dataset1 = new DefaultCategoryDataset();
dataset1.addValue(2000, s, s3);
dataset1.addValue(2500, s, s4);
dataset1.addValue(1500, s, s5);
dataset1.addValue(3000, s, s6);
dataset1.addValue(2800, s, s7);
dataset1.addValue(3300, s, s8);
dataset1.addValue(3500, s, s9);
dataset1.addValue(2500, s, s10);

dataset1.addValue(1000, s1, s3);
dataset1.addValue(800, s1, s4);
dataset1.addValue(600, s1, s5);
dataset1.addValue(700, s1, s6);
dataset1.addValue(750, s1, s7);
dataset1.addValue(850, s1, s8);
dataset1.addValue(1200, s1, s9);
dataset1.addValue(900, s1, s10);
//构造数据集2
String name = "支出";
DefaultCategoryDataset dataset2 = new DefaultCategoryDataset();
dataset2.addValue(1500, name, s3);
dataset2.addValue(1600, name, s4);
dataset2.addValue(1200, name, s5);
dataset2.addValue(2000, name, s6);
dataset2.addValue(2200, name, s7);
dataset2.addValue(2500, name, s8);
dataset2.addValue(3000, name, s9);
dataset2.addValue(2000, name, s10);

//创建图形对象
JFreeChart jfreechart = ChartFactory.createBarChart("双轴图形", "姓名", "收入值",
                      dataset1, PlotOrientation.VERTICAL, false, true, false);
jfreechart.setBackgroundPaint(Color.white);
CategoryPlot categoryplot = (CategoryPlot)jfreechart.getPlot();
categoryplot.setBackgroundPaint(new Color(238, 238, 255));
categoryplot.setDomainAxisLocation(AxisLocation.BOTTOM OR RIGHT);
CategoryDataset categorydataset = dataset2;
categoryplot.setDataset(1, categorydataset);
categoryplot.mapDatasetToRangeAxis(1, 1);
CategoryAxis categoryaxis = categoryplot.getDomainAxis();
categoryaxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN 45);
NumberAxis numberaxis = new NumberAxis("支出值");
categoryplot.setRangeAxis(1, numberaxis);
LineAndShapeRenderer lineandshaperenderer =new LineAndShapeRenderer();
lineandshaperenderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator());
categoryplot.setRenderer(1, lineandshaperenderer);
categoryplot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
LegendTitle legendtitle = new LegendTitle(categoryplot.getRenderer(0));
legendtitle.setMargin(new RectangleInsets(2D, 2D, 2D, 2D));
legendtitle.setFrame(new BlockBorder());
LegendTitle legendtitle1 = new LegendTitle(categoryplot.getRenderer(1));
legendtitle1.setMargin(new RectangleInsets(2D, 2D, 2D, 2D));
legendtitle1.setFrame(new BlockBorder());
BlockContainer blockcontainer = new BlockContainer(new BorderArrangement());
blockcontainer.add(legendtitle, RectangleEdge.LEFT);
blockcontainer.add(legendtitle1, RectangleEdge.RIGHT);
blockcontainer.add(new EmptyBlock(2000D, 0.0D));
CompositeTitle compositetitle = new CompositeTitle(blockcontainer);
compositetitle.setPosition(RectangleEdge.BOTTOM);
jfreechart.addSubtitle(compositetitle);

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 600, 400,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="600" height="400" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/multipleAxis/dualAxis.jsp,该例的运行效果如图3-35所示。

图3-35 双轴图形示例

在实际开发过程中,双轴图应用广泛,但还有一些特殊的情况,例如,在一个组合图中需要显示包含有4个时序序列的时序图,如果四者的刻度都不一样,需要显示四个Y轴坐标,这在JFreeChart中可以使用多轴图表示,其开发类似上述的双轴图的开发,有兴趣的读者可以进行更进一步的尝试。

3.4.11 统计图

离散图、衰退图等图形都属于统计图范畴,在JFreeChart中,也可以绘制此类图形,下面来看看在JFreeChart中离散图的绘制。

读者首先在WebRoot目录下建立statistical目录,并建立scatterChart.jsp文件,离散图形数据集的生成相对复杂,在src源码目录下建立statistical目录,并建立ScatterDataset.java文件,用来生成示例中离散图的数据集,ScatterDataset.java文件的内容如下:

package statistical;

import org.jfree.data.*;
import org.jfree.data.xy.AbstractXYDataset;
import org.jfree.data.xy.XYDataset;

/**
* 离散图示例的数据集类.
*/
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               public class ScatterDataset extends AbstractXYDataset implements XYDataset,
        DomainInfo, RangeInfo {
    private static final long serialVersionUID = 1L;

    private Double xValues[][];

    private Double yValues[][];

    private int seriesCount;

    private int itemCount;

    /** Y轴的最小值. */
    private Number domainMin;

    /** Y轴的最大值. */
    private Number domainMax;

    /** Y轴的范围. */
    private Range domainRange;

    /** X轴的最小值. */
    private Number rangeMin;

    /** X轴的最大值. */
    private Number rangeMax;

    /** X轴的范围. */
    private Range range;

    /**
    * 无参数的构造函数
    */
    public ScatterDataset() {
        this(4, 40);
    }

    /**
    * 构造函数.
    * @param i
    * @param j
    */
   public ScatterDataset(int i, int j) {
        xValues = new Double[i][j];
        yValues = new Double[i][j];
        seriesCount = i;
        itemCount = j;
        double d = (1.0D / 0.0D);
        double d1 = (-1.0D / 0.0D);
        double d2 = (1.0D / 0.0D);
        double d3 = (-1.0D / 0.0D);
        for (int k = 0; k < i; k++) {
            for (int l = 0; l < j; l++) {
                double d4 = (Math.random() - 0.5D) * 200D;
                xValues[k][l] = new Double(d4);
                if (d4 < d)
                      d = d4;
                if (d4 > d1)
                      d1 = d4;
                double d5 = (Math.random() + 0.5D) * 6D * d4 + d4;
                yValues[k][l] = new Double(d5);
                if (d5 < d2)
                      d2 = d5;
                if (d5 > d3)
                      d3 = d5;
            }

        }

        domainMin = new Double(d);
        domainMax = new Double(d1);
        domainRange = new Range(d, d1);
        rangeMin = new Double(d2);
        rangeMax = new Double(d3);
        range = new Range(d2, d3);
   }

   public Number getX(int i, int j) {
        return xValues[i][j];
   }

    public Number getY(int i, int j) {
        return yValues[i][j];
    }

    public int getSeriesCount() {
        return seriesCount;
    }

    public Comparable getSeriesKey(int i) {
        return "实例" + i;
    }

    public int getItemCount(int i) {
        return itemCount;
    }

    public double getDomainLowerBound() {
        return domainMin.doubleValue();
    }

    public double getDomainLowerBound(boolean flag) {
        return domainMin.doubleValue();
    }

    public double getDomainUpperBound() {
        return domainMax.doubleValue();
    }

    public double getDomainUpperBound(boolean flag) {
        return domainMax.doubleValue();
    }

    public Range getDomainBounds() {
        return domainRange;
    }

    public Range getDomainBounds(boolean flag) {
        return domainRange;
    }

    public Range getDomainRange() {
        return domainRange;
    }

    public double getRangeLowerBound() {
        return rangeMin.doubleValue();
    }

    public double getRangeLowerBound(boolean flag) {
        return rangeMin.doubleValue();
    }

    public double getRangeUpperBound() {
        return rangeMax.doubleValue();
    }

    public double getRangeUpperBound(boolean flag) {
        return rangeMax.doubleValue();
    }

    public Range getRangeBounds(boolean flag) {
        return range;
    }

    public Range getValueRange() {
        return range;
    }

    public Number getMinimumDomainValue() {
        return domainMin;
    }

    public Number getMaximumDomainValue() {
        return domainMax;
    }

    public Number getMinimumRangeValue() {
        return domainMin;
    }

    public Number getMaximumRangeValue() {
        return domainMax;
    }
}

上述的ScatterDataset类继承类org.jfree.data.xy.AbstractXYDataset,并实现接口org.jfree.data.xy.XYDataset、org.jfree.data.DomainInfo和org.jfree.data.RangeInfo。若读者需要扩展XYDataset,可以采取上述的方式,继承org.jfree.data.xy.AbstractXYDataset类,并通过实现相应接口来实现,JFreeChart提供了很好的扩展性。

接下来在scatterChart.jsp文件中编写代码建立图形,并设置图形渲染器的属性以及Y轴的属性信息,该JSP文件的代码如下:

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="org.jfree.chart.*,
              org.jfree.chart.servlet.ServletUtilities,
              statistical.ScatterDataset,
              org.jfree.chart.plot.PlotOrientation,
              org.jfree.chart.plot.XYPlot,
              org.jfree.chart.renderer.xy.XYDotRenderer,
              org.jfree.chart.axis.NumberAxis"%>
<%
JFreeChart jfreechart = ChartFactory.createScatterPlot("离散图示例", "X","Y",
                      new ScatterDataset(), PlotOrientation.VERTICAL, true, true, false);
//设置图形的渲染器
XYPlot xyplot = (XYPlot) jfreechart.getPlot();
XYDotRenderer xydotrenderer = new XYDotRenderer();
xydotrenderer.setDotWidth(2);
xydotrenderer.setDotHeight(2);
xyplot.setRenderer(xydotrenderer);

//设置Y轴属性
NumberAxis numberaxis = (NumberAxis) xyplot.getDomainAxis();
numberaxis.setAutoRangeIncludesZero(false);

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 400, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="400" height="300" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/statistical/scatterChart.jsp,该例的运行效果如图3-36所示。

图3-36 离散图示例

3.4.12 实验图

在JFreeChart中,还提供了一些高级功能,来绘制实验图标,例如刻度盘、钟表图等,显示效果很好,下面来看看在JFreeChart中如何绘制钟表图。

首先,在WebRoot目录下建立experimental目录,并建立dailChart.jsp文件,在该文件中编写代码展示常见的钟表图形。与绘制刻度图相关的java类位于org.jfree.exper imental.chart.dial包内。可读图图形类为:org.jfree.experimental.chart.dial.DialPlot,指针类为:org.jfree.experimental.chart.dial.DialPointer。

dailChart.jsp文件的代码如下:

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="org.jfree.chart.JFreeChart,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.data.general.DefaultValueDataset,
              org.jfree.experimental.chart.plot.dial.*,
              java.awt.*,
              org.jfree.ui.StandardGradientPaintTransformer,
              org.jfree.ui.GradientPaintTransformType"%>

<%
//创建数据集
DefaultValueDataset hoursDataset = new DefaultValueDataset(6D);
DefaultValueDataset dataset2 =new DefaultValueDataset(15D);

//设置钟表图形属性
DialPlot dialplot = new DialPlot();
dialplot.setView(0.0D, 0.0D, 1.0D, 1.0D);
dialplot.setDataset(0, hoursDataset);
dialplot.setDataset(1, dataset2);
SimpleDialFrame simpledialframe = new SimpleDialFrame();
simpledialframe.setBackgroundPaint(Color.lightGray);
simpledialframe.setForegroundPaint(Color.darkGray);
dialplot.setDialFrame(simpledialframe);
//设置图形背景
DialBackground dialbackground =new DialBackground(Color.yellow);
dialbackground.setGradientPaintTransformer(new
StandardGradientPaintTransformer(GradientPaintTransformType.VERTICAL));
dialplot.setBackground(dialbackground);
StandardDialScale standarddialscale = new StandardDialScale(0.0D, 12D, 90D, -360D);
standarddialscale.setFirstTickLabelVisible(false);
standarddialscale.setMajorTickIncrement(1);
//设置表刻度和数字离中心的半径
standarddialscale.setTickRadius(0.90D);
//设置数字离开表盘的距离
standarddialscale.setTickLabelOffset(0.1D);
standarddialscale.setTickLabelFont(new Font("宋体", 0, 14));
dialplot.addScale(0, standarddialscale);
StandardDialScale standarddialscale1 = new StandardDialScale(0.0D, 60D, 90D, -360D);
standarddialscale1.setVisible(false);
standarddialscale1.setMajorTickIncrement(5D);
standarddialscale1.setTickRadius(0.68D);
standarddialscale1.setTickLabelOffset(0.15D);
standarddialscale1.setTickLabelFont(new Font("宋体", 0, 14));
dialplot.addScale(1, standarddialscale1);

//设置时针的指针
org.jfree.experimental.chart.plot.dial.DialPointer.Pointer pointer =new
                                          org.jfree.experimental.chart.plot.dial.DialPointer.Pointer(0);
pointer.setRadius(0.5D);
dialplot.addLayer(pointer);
dialplot.mapDatasetToScale(1, 1);

//设置分针的指针
org.jfree.experimental.chart.plot.dial.DialPointer.Pointer pointer1 =new
                                          org.jfree.experimental.chart.plot.dial.DialPointer.Pointer(1);
pointer1.setRadius(0.7D);
dialplot.addLayer(pointer1);

//设置刻度中间的圆圈的大小
DialCap dialcap = new DialCap();
dialcap.setRadius(0.05D);
dialplot.setCap(dialcap);

//根据刻度图形建立JFreeChart对象
JFreeChart jfreechart = new JFreeChart(dialplot);
//设置图形的标题
jfreechart.setTitle("刻度图示例——钟表图");

String filename = ServletUtilities.saveChartAsPNG(jfreechart, 300, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="300" height="300" border="0" usemap="#<%= filename %>">

在浏览器中输入地址http://localhost:8080/jfreechart/experimental/dialChart.jsp,运行效果如图3-37所示。

图3-37 钟表图示例

钟表图的表盘是圆形的,但是在实验室的一些仪表图中,常见的有一些其他形状的图形,如刻度图,如图3-38所示。

图3-38 刻度图示例

要实现上面图形的效果,读者在WebRoot\experimental目录下建立dialChart2.jsp文件,该文件的内容如下:

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="org.jfree.chart.JFreeChart,
              org.jfree.chart.servlet.ServletUtilities,
              org.jfree.data.general.DefaultValueDataset,
              org.jfree.experimental.chart.plot.dial.*,
              java.awt.*,
              org.jfree.ui.StandardGradientPaintTransformer,
              org.jfree.ui.GradientPaintTransformType"%>


<%
//构造数据集
DefaultValueDataset dataset = new DefaultValueDataset(50D);

//设置刻度图形属性
DialPlot dialplot = new DialPlot();
dialplot.setView(0.2D, 0.0D, 0.58D, 0.3D);
dialplot.setDataset(dataset);

//设置刻度面板
StandardDialFrame standarddialframe = new StandardDialFrame(60D, 60D);
//设置刻度面板的内径
standarddialframe.setInnerRadius(0.7D);
//设置刻度面板的外径
standarddialframe.setOuterRadius(0.95D);
//设置面板的前景颜色
standarddialframe.setForegroundPaint(Color.gray);
//设置面板的粗细
standarddialframe.setStroke(new BasicStroke(2F));
dialplot.setDialFrame(standarddialframe);
GradientPaint gradientpaint = new GradientPaint(new Point(),
                  new Color(255, 255, 255),
                  new Point(),
                  new Color(230, 230, 230));

//设置刻度的背景
DialBackground dialbackground =new DialBackground(gradientpaint);
dialbackground.setGradientPaintTransformer(new
StandardGradientPaintTransformer(GradientPaintTransformType.VERTICAL));
dialplot.addLayer(dialbackground);
StandardDialScale standarddialscale = new StandardDialScale(0.0D, 75D, 115D, -50D);
standarddialscale.setTickRadius(0.88D);
standarddialscale.setTickLabelOffset(0.07D);
standarddialscale.setMajorTickIncrement(25D);
standarddialscale.setTickLabelPaint(null);
dialplot.addScale(0, standarddialscale);
org.jfree.experimental.chart.plot.dial.DialPointer.Pin pin = new
                                                org.jfree.experimental.chart.plot.dial.DialPointer.Pin();
pin.setRadius(0.82D);
dialplot.addLayer(pin);

//根据可读图形构造JFreeChart对象
JFreeChart jfreechart = new JFreeChart(dialplot);
//设置图形标题
jfreechart.setTitle("刻度图示例");
String filename = ServletUtilities.saveChartAsPNG(jfreechart, 400, 300,null, session);
String graphURL = request.getContextPath()+ "/DisplayChart?filename=" + filename;
%>
<img src="<%= graphURL %>" width="400" height="300" border="0" usemap="#<%= filename %>">

在浏览器中输入地址:http://localhost:8080/jfreechart/experimental/dialChart2.jsp,即可看到图3-38所示的刻度图。

3.5 原理分析

在Java中,图形的API一般是用AWT和Swing两个包来实现的。在JFreeChart1.0.6中,跟绘制图形密切相关的部分位于org.jfree.chart.plot包下,查看该包下的Plot、PiePlot等类的源码后,可以看到,这些类都引用了AWT包和Swing包,由此可知,JFreeChart的Web图形只是在AWT和Swing包的基础上进行了进一步封装来实现的。

接下来读者将学习一下sun公司AWT和Swing包中JFreeChart主要用到的部分,来对JFreeChart进行更加深入的探讨。

3.5.1 AWT

抽象窗口工具包AWT(Abstract Window Toolkit)是JDK提供的建立图形用户界面GU(I Graphics User Interface)的工具集,AWT可用于Java的applet和应用程序中。它支持图形用户界面编程的功能包括:用户界面组件;事件处理模型;图形和图像工具,包括形状、颜色和字体类;布局管理器,可以进行灵活的窗口布局,而与特定窗口的尺寸和屏幕分辨率无关。

AWT提供了一个丰富的图形环境,尤其是在Java 1.2及其以后版本中更是如此。通过Graphics2D对象和Java2D、Java3D服务,我们可以创建很多功能强大的图形应用程序,例如画图和制表包。但是AWT包也有一些缺点,那就是这个GUI的外观和行为在不同的主机上的表现会有所不同。

在JFreeChart1.0.6中,用到了AWT包的很多类和接口。例如,在org.jfree.chart.plot.Plot类中,用到的AWT的类或接口有:java.awt.AlphaComposite、java.awt.BasicStroke、java.awt.Color、java.awt.Composite、java.awt.Font、java.awt.GradientPaint、java.awt.Graphics2D、java.awt.Image、java.awt.Paint、java.awt.Shape、java.awt.Stroke、java.awt.geom.Ellipse2D、java.awt.geom.Point2D和java.awt.geom.Rectangle2D。

在JFreeChart1.0.6中,用到了AWT包的诸多接口来生成图形对象。

3.5.2 Swing

Swing是Sun公司试图解决AWT缺点的一个尝试。它是在AWT的基础是构建的,是Sun公司开发的一个经过仔细设计的、灵活而强大的GUI工具包。Swing使用了AWT的事件模型和支持类,例如Colors、Images和Graphics。Swing的组件集比AWT提供的组件集更为广泛。

为了克服AWT的缺点,Swing将对主机控件的依赖性降至了最低。它只为诸如窗口和框架之类的顶层组件使用对等体。大部分组件(JComponent及其子类)都是使用纯Java代码来模拟的。因此Swing天生就可以在所有主机之间很好地进行移植。

在JFreeChart 1.0.6中,Swing用得比较少,但也在很多相关的图形类中应用,例如,在org.jfree.chart.plot.Plot和org.jfree.chart.plot.Marker类中,也用到Swing的EventListenerList类,用来作为事件监听器的列表。org.jfree.chart.plot.JThermometer类继承javax.swing.JPanel类。

3.5.3 关键源码剖析

首先看一下JFReeChart的源码中一个重要的抽象类Plot,它是PiePlot等类的基类。在该类中的draw(…)方法与图形的生成有着密切的关系。该方法的定义如下:

public abstract void draw(Graphics2D g2,
                          Rectangle2D area,
                          Point2D anchor,
                          PlotState parentState,
                          PlotRenderingInfo info);

该方法在抽象类Plot中并没有提供实现,但是因为该方法是abstract类型的,所以所有实现该抽象类的方法都必须实现这个方法。下面打开PiePlot的源码,在该类中,draw(…)方法的实现如下:

public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
                  PlotState parentState, PlotRenderingInfo info) {

      // adjust for insets...
      RectangleInsets insets = getInsets();
      insets.trim(area);

      if (info != null) {
          info.setPlotArea(area);
          info.setDataArea(area);
      }

      drawBackground(g2, area);
      drawOutline(g2, area);

      Shape savedClip = g2.getClip();
      g2.clip(area);

      Composite originalComposite = g2.getComposite();
      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC OVER,
              getForegroundAlpha()));
      if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
          drawPie(g2, area, info);
      }
      else {
          drawNoDataMessage(g2, area);
      }

      g2.setClip(savedClip);
      g2.setComposite(originalComposite);

      drawOutline(g2, area);
}

该方法的第三个参数的类型分别为java.awt.Graphics2D、java.awt.geom.Rectangle2D和java.awt.geom.Point2D类,这些类都属于AWT包中的类,其实归根到底,JFreeChart也只是在AWT和Swing上再包装了一层而已,它提供了一些常用图形的API,例如饼图、柱状图、曲线图、时序图和甘特图等,使得我们生成常用图形更加方便,不用读者自己调用AWT来完成这些功能。

3.6 小结

安装与配置JFreeChart是相当简便的。在需要使用JFreeChart制作各种图表的Web工程中,需要在工程的web.xml中配置JFreeChart对应的Servlet。

JFreeChart中牵涉到的重要的概念包括:数据集(dataset)、图形(plot)、渲染(renderer)、轴(axis)和标题(Title),这些都是JFreeChart的核心类。

使用JFreeChart可以开发各种各样的图表,例如柱状图、饼图、时序图、甘特图和区域图等。在JFreeChart中,还提供了通过JDBC从数据库中提取数据填充Dataset的功能,可以填充的数据集包括饼图、柱状图和曲线图的数据集。

JFreeChart生成图表时,底层依赖的是Java的AWT和Swing这两个图形包。在JFreeChart中,与生成图形最密切关联的抽象类是Plot类,它是其他图形类(例如PiePlot类)的基类,图形类中的draw(…)方法与图形的生成有着密切的关系,在了解JFreeChart生成图表的原理时,读者可重点阅读各图形类的draw(…)方法。