JFreeChart自学整理

本文是作者自学JFreeChart的总结,探讨如何利用数据创建图表,并提供了一些常见问题的解决方案,如数据类型匹配、图形配置、Web项目中的Servlet配置等。还介绍了增加图片交互性的方法,如使用tooltips和urls实现事件关联。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(JFreeChart我就不多介绍了,用过的人都知道它是什么。网上关于这方面的博客也是多得数不胜数,不过感觉多数都是互相抄袭,这点真是不好,浪费后来人的时间,想找点有用的真是难啊,所幸的是还是多少有些有用的,至少教会我在开始阶段如何画出一个漂亮图形出来。这里贴一些网上没有找到的,我看源码总结出来的东西,希望对后来人有所助益,同时也是对自己的理解进行一次剖析和检查,纯属个人自学总结,错误在所难免,欢迎指教,共同探讨)

首先一个图要表达某个意思最重要的肯定是数据,下面就先从数据开始分析:

0维键值对:KeyedValue
继承于Value,添加了一个对应的关键字(Key)。
默认实现DefaultKeyedValue中具有私有成员Comparable key和Number value,这中对应关系不多说了。
1维数值:Values
拥有一个一维数值型数组,从这些数据里我们没法知道这些数据所代表的意义是什么。我们可以把它想象成一个List<Number>,但不要认为它就是一个List,它只是一个接口,没有任何实现。
1维键值对:KeyedValues
继承于Values,为每个Value增加了对应的关键字(Keys),此时我们能够明白每个数据值对应的意义(即Key)。它是个什么东西呢?它也是个接口。如果为了理解的方便,暂且也可以把它想象成一个HashMap<Key, Value>,但也请一定记得它绝对不是HashMap,甚至它的实现类的实现方式和HashMap的实现方式完全不一样。
可以看到它的默认实现DefaultKeyedValues中,分别用了一个ArrayList 来保存keys 和values ,甚至还另外添加了一个HashMap来专门保存key值及其索引(这个在编者我看来是怎么也想不通的,感觉真的没有必要,key都保存在了List里,索引自然也是可以根据indexOf方法获取,如果说是因为HashMap里有key值唯一性,List没有,那样也是可以用indexOf方法先去找达到相同的效果的,想不通,想不通了???)
2维数值:Values2D
此时相当于一个二维数组,有行有列,可以获取行数和列数,并且可以根据给定行列值获取对应数据值。这里的二维怎么表示呢?用二维数组?当然可以实现,可是会不会方便呢?
2维键值对:KeyedValues2D
同样,继承于Values2D的它自然也是有了键值对,不多啰嗦,看它默认实现。
在DefaultKeyedValues2D中三个私有List和一个用于判断是否排列关键字的bool值(这个暂时别管了)。它是如何实现二维键值对的呢?当然是靠这三个List,想想KeyedValues的实现方式,它们肯定神似。不卖关子了,它是用一个List保存rowKeys,用另一个保存KeyedValues(KeyedValues可以有columnKeys和Values的对应关系),成了。不要怀疑还有一个List哪去了,那个List用来保存columnKeys了。

Dataset数据集,字面翻译过来就这样叫。这个可称得上最底层的接口做了什么呢?它就标明所有的图表数据集都必须有一个事件监听器和一个数据集ID...
根据数据类型生成不一样的图数据集,与KeyedValue生成的默认的类DefaultKeyedValueDataset私有成员就是一个KeyedValue;与KeyedValues生成的默认的类DefaultKeyedValuesDataset就直接派生于DefaultPieDataset,DefaultPieDataset中的私有成员就是一个DefaultKeyedValues(饼图的数据确实是一维键值对,很符合这种数据类型,可是插在这里让我感到很不解,按理放在DefaultKeyedValuesDataset不是让人更好理解吗?);而DefaultKeyedValues2DDataset则派生于DefaultCategoryDataset,DefaultCategoryDataset中的私有成员则是一个DefaultKeyedValues2D(还是一样的,又有插队的……)
不同的数据类型适合不同类型的图,DefaultKeyedValueDataset只有一个数据我还真的想不出适合什么图,或许可以作为一般的说明图,直接在图旁边标识;而DefaultKeyedValuesDataset适合的图当然是饼图了,而最后一种适合的就是柱状图。
肯定有朋友会问折线图的数据类型了,折线图的确太普遍了,很多地方都用(先不管X唯一不唯一),同样我们可以用一个List保存X值,另一个List保存Y值,似乎可以,同样的多折线图,可以用KeyedValues2D对应的方法,一个List保存折线键值,另一个List保存一条折线的数据,是不是真的没有问题?是不是真的是这样实现的呢?
有没有问题我不知道,我可以告诉你的是它不是这样实现的,而是换了一种思维(这样看来我的思维还是太死板了,不会变通)。
它是如何实现的呢?
从最底层,‘DataItem系列’,其实没有这个类或者接口,但有相关的类,如XYDataItem、TimeSeriesDataItem……XYDataItem中两个数值型私有成员,分别用来保存X、Y值,而TimeSeriesDataItem一个RegularTimePeriod类型,一个Number类型,分别用来保存时间和数值。(RegularTimePeriod的子类有Year, Quarter, Month, Week, Day, Minute, Hour, Minute, Second, Millisecond, FixedMillisecond, 具体实现再研究吧)
这样用‘DataItem’就可以保存一个点了,再用一个List来保存'DataItem'就可以保存一条折线上的数据了。不要以为这里又用另一个List来保存多条折线了,这里没有那样做,我也没弄明白不那样做的原因。这里用的是Series类保存一条折线,一个Series里总包含有一个key值,然后就是数据List了,如XYSeries, TimeSeries ...
Series配合Dataset就形成了‘SeriesCollection’就是所谓折线图了,如XYSeriesCollection, TimeSeriesCollection.
Series:私有成员:Comparable key唯一标识一条折现;String description 描述这条折现,默认为null.
其子类
XYSeries:私有成员:List data 保存XYDataItem; int maximumItemCount:设置最大数据个数,调用set方法设置; boolean autoSort 是否自动排序,默认为true; boolean allowDuplicateXValue 是否允许重复X值(意思即一个X值对应多个Y值);(这两个boolean默认都为true,只可以通过构造方法设置)
TimeSeries:私有成员:String domain 描述横坐标,默认”Time”; String range 描述纵坐标,默认”Value”;(domain和range都不会在图片中表现出来) Class timePeriodClass 保存时间类型; List data 保存TimeSeriesDataItem; int maxmumItemCount 设置最大数据个数

分析了数据,接下来就开始正式的图形与数据之间的关系了,因为要对号入座,图形用错了数据类型自然会报异常,我就列出它们之间的一些继承关系,从名字上应该也能或多或少有些理解。

Dataset -> AbstractDataset  SeriesDataset -> AbstractSeriesDataset ( XYDataset ) ->  AbstractXYDataset

Dataset -> AbstractDataset
Dataset KeyedValue ->  KeyedValueDataset 
AbstractDataset KeyedValueDataset -> DafaultKeyedValueDataset


Dataset -> AbstractDataset
Dataset KeyedValues -> PieDataset
PieDataset -> KeyedValuesDataset (二者并无不同)
AbstractDataset PieDataset  -> DefaultPieDataset
DefaultPieDataset KeyedValuesDataset -> DefaultKeyedValuesDataset

Dataset -> AbstractDataset
Dataset -> CategoryDataset
AbstractDataset CategoryDataset -> DefaultCategoryDataset


Dataset -> AbstractDataset
Dataset -> SeriesDataset
SeriesDataset -> XYDataset
XYDataset -> IntervalXYDataset
AbstractDataset SeriesDataset -> AbstractSeriesDataset
AbstractSeriesDataset XYDataset -> AbstractXYDataset
AbstractXYDataset IntervalXYDataset -> AbstractIntervalXYDataset
AbstractIntervalXYDataset -> TimeSeriesCollection XYSeriesCollection

好了,有些心急的童鞋估计都等得不耐烦了,说了这么多还没有说到底该如何用。其实到底如何用,网上这方面的资料多得很,多啰嗦只是对各位的时间的浪费。有兴趣的就接着往下看。

首先当然是下载包了,需要的有"gnujaxp.jar","jcommon.jar","jfreechart.jar",加到项目里……

如果你是web项目,当然还得配置下Servlet,当然啦,有能力的可以自己写Servlet.

        在web.xml中添加

        <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>/servlet/DisplayChart</url-pattern>
	</servlet-mapping>
        或者自己写Servlet如下:(配置就不写了)

package com.esen.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jfree.chart.JFreeChart;

import com.keypoint.PngEncoder;

public class ChartViewer extends HttpServlet {
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		HttpSession session = request.getSession();
		JFreeChart chart = (JFreeChart)session.getAttribute("chart");
		if(chart == null)
			return;
		session.removeAttribute("chart");
		response.setContentType("image/png");
		PngEncoder encoder = new PngEncoder(chart.createBufferedImage(760, 360, null), false, 0, 9);
		response.getOutputStream().write(encoder.pngEncode());
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}
ok,现在可以开始JFreeChart的画图了,没有特殊需求的话,一般都可以调用JFreeChart提供的ChartFactory的静态方法来产生相应的图。
例子:

     //饼图

       ChartFactory.createPieChart(title, dataset, legend, tooltips, urls);   // (参数介绍:title - String类型,图标题;dataset - PieDataset,还不知道的,看上面的介绍; legend - boolean类型,是否产生说明信息; tooltips - boolean类型,是否产生工具提示; urls - boolean类型,是否产生链接;tooltips 和 urls 均用于web项目中的操作,如果不是Web项目,可以设置为false)

     //柱图

       ChartFactory.createBarChart(title, categoryAxisLabel, valueAxisLabel, dataset, orientation, legend, tooltips, urls);   //  (参数介绍: categoryAxisLabel - String 类型,横坐标标签; valueAxisLabel - String 类型,纵坐标标签, orientation - PlotOrientation类型,有PlotOrientation.HORIZONTAL和PlotOrientation.VERTICAL两种类型,分别标识图形方向为垂直和水平,其它参数同上)

    //线图

       ChartFactory.createLineChart(title, categoryAxisLabel, valueAxisLabel, dataset, orientation, legend, tooltips, urls)

    //时间序列图

       ChartFactory.createTimeSeriesChart(title, timeAxisLabel, valueAxisLabel, dataset, legend, tooltips, urls);  //  (参数介绍:  timeAxisLabel - String 横坐标标签; 其它参数同上)

      ……

创建好图之后,如果不是Web项目,直接调用ChartUtilities.writeChartAsJPEG(outputStream, chart, width, height);就可以画出图片来了

否则,就先调用

ServletUtilities.saveChartAsJPEG(chart, width, height, session); 

保存图片到Session里,然后在页面里调用

<img src=" <%=request.getContextPath() + ‘/servlet/DisplayChart?filename=’ + filename%>

打开页面就可以显示所创建的图片了,当然这里的Servlet如果是自己写的,路径得相应的作下修改。

但是, 这样画出来的图片只是简单的图片,没有任何的交互性可言,也就是没有任何事件与该图片关联。学过DOM的都知道img有个map,可以产生相应的事件,这就要用到前面介绍的参数中的tooltips和urls了,urls是产生相应的点击链接事件,而tooltips则可以产生HTML中的TITLE显示信息,或者产生onMouseOver和onMouseOut事件。怎么用?上代码:

        ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());
	String filename = ServletUtilities.saveChartAsPNG(chart, width, height, info, session);//注意多了个info,这个info用来保存所有的MAP信息	
	ChartUtilities.writeImageMap(out, filename, info, true);//out当然是页面的out,最后一个参数为true是就是阐述onMouse事件,否则产生TITLE提示
成了,不信可以去看看……



下面就讲些常用的设置吧(以柱状图为例),毕竟设置不对,效果很不好,比如最常用的就是字体,JFreeChart默认字体好像不支持中文,不设置的话会乱码,至少我所用的版本是这样的。

        //设置图片标题
        TextTitle title = chart.getTitle();
        title.setText("图片示例");//设置标题文本
        title.setPaint(Color.black);//设置标题字体颜色
        title.setBackgroundPaint(Color.cyan);//设置背景颜色
        title.setExpandToFitSpace(true);//设置标题宽度自动适应为图片宽度
        title.setHeight(20);//设置标题高度
        title.setToolTipText("这是一个提示");//设置标题提示,和tooltips真假有关系,用于Web
        title.setURLText("http://www.csdn.com");//设置标题链接,和urls有关,用于Web
        title.setFont(new Font("黑体", Font.BOLD, 18));//设置标题字体
        //设置轴字体
        CategoryAxis domainAxis = plot.getDomainAxis();//横轴
        domainAxis.setLabelFont(new Font("楷体", Font.PLAIN, 12));
        domainAxis.setTickLabelFont(new Font("宋体", Font.CENTER_BASELINE, 14));

        ValueAxis rangeAxis = plot.getRangeAxis();//纵轴
        rangeAxis.setLabelFont(new Font("楷体", Font.PLAIN, 12));
        rangeAxis.setTickLabelFont(new Font("宋体", Font.CENTER_BASELINE, 14));
        //设置图例说明字体
        LegendTitle legend = chart.getLegend();
        legend.setItemFont(new Font("宋体", Font.ITALIC, 12));
       //设置图片renderer
        CategoryItemRenderer renderer = plot.getRenderer();
        
        //设置标签显示
        renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{1}:({0},{2})", NumberFormat.getInstance()));
        renderer.setBaseItemLabelsVisible(true);
        renderer.setBaseItemLabelFont(new Font("宋体", Font.PLAIN, 12));
        renderer.setBaseItemLabelPaint(Color.WHITE);
        ItemLabelPosition position = new ItemLabelPosition(ItemLabelAnchor.OUTSIDE1, TextAnchor.TOP_CENTER);
        renderer.setBasePositiveItemLabelPosition(position);
        
        //设置图片链接
        CategoryURLGenerator generator = new StandardCategoryURLGenerator("index.html", "param1", "param2");
        renderer.setBaseItemURLGenerator(generator);
        
        //设置工具提示(网页项目中的title效果)
        CategoryToolTipGenerator generatorToolTip = new StandardCategoryToolTipGenerator("{0}{1}{2}", NumberFormat.getInstance(Locale.ENGLISH));
        renderer.setBaseToolTipGenerator(generatorToolTip);

附加:(对于图片链接及工具提示的一些详解)

URL
CategoryURLGenerator		URL for CategoryDataset
StandardCategoryURLGenerator		标准Category数据集URL生成器
	私有成员:String prefix 前缀,即网址部分,默认“index.html”; String seriesParameterName 系列参数名,默认“series”; String categoryParameterName 类别参数名,默认“category”;通过构造函数进行设置。
最后生成的URL是“index.html?series=’rowKey’&category=’columnKeys’”
CustomCategoryURLGenerator		自定义Category数据集URL生成器
	私有成员:ArrayList urlSeries 保存一系列List,每个List都保存有相应Series的所有url,具体用法有待研究。

XYURLGenerator			URL  for XYDataset
StandardXYURLGenerator			标准XY数据集URL生成器
	私有成员:String prefix 前缀,默认“index.html”; String seriesParameterName 系列参数名,默认“series”; String itemParameterName item参数名,默认“item”;通过构造函数设置。同StandardCategoryURLGenerator.
CustomXYURLGenerator			自定义XY数据集URL生成器
 同CustomCategoryURLGenerator
TimeSeriesURLGenerator			时间序列数据集URL生成器
	私有成员:DateFormat dateFormat 时间格式; 接下来三个同StandardXYURLGenerator

Label	 Category
AbstractCategoryItemLabelGenerator
	私有成员:String labelFormat 标签格式; String nullValueString 替代value为空时的值; NumberFormat numberFormat 数值格式; DateFormat dateFormat 日期格式; NumberFormat percentFormat 百分数格式。
	{0}=rowKey, {1} = columnKey, {2} = value, {3} = value所占百分数

StandardCategoryItemLabelGenerator		标准Category标签生成器
	继承自AbstractCategoryItemLabelGenerator, 默认的标签格式为”{2}”.
IntervalCategoryItemLabelGenerator		间隔Category标签生成器
	继承自StandardCategoryItemLabelGenerator, 默认标签格式”({0}, {1}) = {3} – {4}”
	{0} = rowKey; {1} = columnKey; {2} = value; {3} = startValue; {4} = endValue.

ToolTip		Category
CategoryToolTipGenerator
StandardCategoryToolTipGenerator	标准Category工具提示生成器
	继承自AbstractCategoryItemLabelGenerator,默认的工具提示格式为”({0}, {1} ={2})”
IntervalCategoryToolTipGenerator	间隔Category工具提示生成器
	默认的工具提示格式为”({0}, {1}) = {3} – {4}”

Label		XY
AbstractXYItemLabelGenerator
	私有成员:String formatString 字符串格式; NumberFormat xFormat 数值型x值格式; DateFormat xDateFormat 日期型y值格式; NumberFormat yFormat 数值型y值格式; DateFormat yDateFormat 日期型y值格式; String nullYString y值为空时的替换字符串,默认“null”
	{0} = seriesKey, {1} = rowValue, {2} = columnValue

StandardXYItemLabelGenerator		标准XY标签生成器
	继承自AbstractXYItemLabelGenerator, 默认标签格式为”{2}”
IntervalXYItemLabelGenerator		间隔XY标签生成器
	继承自AbstractXYItemLabelGenerator, 默认标签格式为”{5} – {6}”
	{0} = seriesKey; {1} = xValue; {2} = xStart; {3} = yEnd; {4} = yValue; {5} = yStart; {6} = yEnd.

ToolTip	 	XY
XYToolTipGenerator
StandardXYToolTipGenerator
	默认的提示格式为”{0}: ({1}, {2})”.



(还在研究阶段,有新的理解会及时添加进来,有错误的地方,还请看到的朋友友善指正)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值