JFreeChart 高级应用 一 图表条目标签(Item Label)
1、简介
1.1 概述
对于大多数的图表类型来说,JFreeChart允许我们在图表的每个条目上、或者内部、或者附近显示条目标签。例如,下图在每个条形图上显示出了真实的值。
本文主要讲述:
l如何让条目标签可视(仅限于支持条目标签的图表类型)
l如何改变条目标签的外观(字体和颜色)
l如何指定条目标签的位置
l如何定制条目标签的文本
忠告:我们使用上面的特征时,要谨慎。图表是期望用来分析总结数据的——如果我们觉得在图表上显示真实数据是非常有必要的话,那我们的数据应使用一个表格格式显示更为合适。
1.2 局限性
在当前版本JFreeChart中,条目标签的使用是有很多局限性的:
l一些renderer不支持条目标签
l轴范围的自动调节,忽略了条目标签的自动调整——如果图表的周围没有足够的空间(使用方法setUpperMargin()或setLowerMargin()进行了相应的调整),那么一些图表条目标签在图表上显示不出来。
相信,在以后的JFreeChart版本中,这些限制问题将被解决。
2、显示条目标签
2.1 概述
条目标签默认是不显示的,因此我们需要使用renderer进行创建和显示条目标签。这主要有以下两个步骤:
l分配一个CategoryItemLabelGenerator或XYItemLabelGenerator给renderer—这是一个负责创建标签的对象。
l在renderer里面设置一个标签可视的标志。可以针对全部系列进行设置,也可以针对具体的每一个系列进行设置。
此外,我们可以定制条目标签的位置、字体和颜色。在下面的章节里我们将详细的介绍。
2.2 创建一个条目标签并赋值
使用renderer分配的一个标签产生器创建条目标签(这与图表工具条的机制是相同的)。
下面代码说了将一个标签产生器指派给CategoryItemRenderer:
CategoryItemRenderer renderer = categoryplot.getRenderer(); CategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator("{2}", new DecimalFormat("0.00")); renderer.setBaseItemLabelGenerator(generator);
同样的,将一个产生器指派给XYItemRenderer,代码如下:
XYPlot plot = (XYPlot) jfreechart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); XYItemLabelGenerator generator = new StandardXYItemLabelGenerator("{2}", new DecimalFormat("0.00"), new DecimalFormat("0.00")); renderer.setBaseItemLabelGenerator(generator);
我们可以在标准产生器的构造函数中定制不同的行为。当然了,我们也可以创建我们总计的产生器,详见5.2章节。
2.3 所有的系列显示条目标签
方法renderer.setBaseItemLabelsVisible(false)是控制着条目标签的显示。对于CategoryItemRenderer:
CategoryItemRenderer renderer = categoryplot.getRenderer(); renderer.setBaseItemLabelsVisible(true);
同样对于:XYItemRenderer
XYItemRenderer renderer = categoryplot.getRenderer(); renderer.setBaseItemLabelsVisible(true);
一旦设置,这个标志优先管理我们在所有地方对每一系列做的设置,主要为了应用每一系列的设置。我们可以设置个标志为null(见2.4章节)
2.4 为选择的系列显示条目标签
我们可以控制图表的每一个系列的条目标签是否显示。例如:如下图仅显示第一系列条目标签。
v
下面代码可以设置如上效果:
CategoryItemRenderer renderer = categoryplot.getRenderer(); renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator()); renderer.setBaseItemLabelsVisible(null); // clears the ALL series flag renderer.setSeriesItemLabelsVisible(0, true); renderer.setSeriesItemLabelsVisible(1, false);
注意:上面代码中对全部的系列设置为null—这一点非常重要,因为全部系列的标志控制每一个系列的标志。
2.5 问题与解决
如果按照上面的步骤操作,你仍然未看见条目标签显示在图表上,那么我们从以下几个方面进行考虑:
lRenderere必须需要一个标签产生器——这是一个用来创建每一个标签的文本条目的对象。
l一些renderer不支持条目标签(具体参考renderer相关的文档)
3、条目标签外观
3.1 概述
我们可以通过改变条目的颜色、字体来改变图表条目标签的外观。正如其他renderer属性一样,属性的设置可以是全部的系列,可以是具体某一系列。
在JFreeChart目前的版本中,标签是月年个一个透明的背景画出来的。我们不能设置标签的背景颜色,也不能指定标签的边框。这些在以后的版本中会得到解决。
3.2 改变条目标签字体
为了在所有的系列中改变条目标签的字体,我们可以使用下面的代码:
CategoryItemRenderer renderer = categoryplot.getRenderer(); renderer.setBaseItemLabelFont(new Font("黑体", Font.PLAIN, 20));
同样,也可以为单个系列设置字体:
//add settings for individual series... renderer.setSeriesItemLabelFont(0, new Font("SansSerif", Font.PLAIN, 20)); renderer.setSeriesItemLabelFont(1, new Font("SansSerif", Font.PLAIN, 10));
注意:renderer.setBaseItemLabelFont(null)方法会出错。开发指南显示的代码有错误。
3.3 改变条目标签的颜色
改变条目标签的颜色,我们可以使用下面的代码:
CategoryItemRenderer renderer = categoryplot.getRenderer();
renderer.setBaseItemLabelPaint(Color.red);
同样的,可以为单独每一系列设置颜色:
//add settings for individual series... renderer.setSeriesItemLabelPaint(0, Color.red); renderer.setSeriesItemLabelPaint(1, Color.blue);
注意:renderer.setBaseItemLabelPaint(null);方法会出错。开发指南显示的代码有错误。
4、条目标签的位置
4.1 概述
条目标签的位置是通过ItemLabelPosition对象的四个属性来控制的。
我们可以通过接口CategoryItemRenderer的方法来独立定义条目标签的正负点位置:
public void setBasePositiveItemLabelPosition(ItemLabelPosition position); public void setBaseNegativeItemLabelPosition(ItemLabelPosition position);
理解这些属性如何影响独立标签的最终位置的关键是了解JFreeChart里面条目标签的特征。四个特征是:
l条目标签点——决定标签的起始位置
l文本点——标签里的文本相对于条目标签的位置。
l旋转点——标签文本旋转的点位置
l旋转角度——标签的旋转角度。
这些的详细描述在下一章详细介绍。
4.2 条目标签的位置
设置条目标签位置的目的,主要是为了找出标签在图表上贴向数据条目的一个点(x,y)位置。同时在画图表时,该标签也被画在该点处。更多的信息可以参考ItemLabelAnchor文档。
4.3 标签文本的位置
标签文本的位置,主要取决于上节讲的标签位置。我们可以讲标签文本在标签里设置在右上部、或左下部等,更多的信息参见TextAnchor文档。
运行JCommon包内的org.demo.package下面的DrawStringDemo应用,可以更好的理解标签文本在标签内是如何放置的。
4.4 标签旋转点
在标签上定义了一个旋转点,用于旋转标签。在DrawStringDemo实例中很好演示了这个特征。
4.5 标签旋转角度
旋转角度定义了标签沿旋转点旋转的角度。该角度为弧度。
5、定制条目标签文本
5.1 概述
定制条目标签文本,我们需要依赖用JFreeChart里的标签产生器来为条目标签创建文本。如果要想完全控制标签文本的控制,我们就需要编写自己的标签产生器,需要实现接口CategoryItemLabelGenerator。
在本章节里,我们对自定义标签器技术做了简要的讲述,然后用两个实例来说明该技术过程。
5.2 实现一个自定义的标签生成器
开发一个自定义标签产生器,我们需要写一个类,该类必须实现CategoryItemLabelGenerator接口里的方法。
public String generateLabel(CategoryDataset dataset, int series,int category)
该renderer调用该方法获得一个标签的字符串,并且将该字符串传入到当前条目的CategoryDataset、序列和种类。这就意味着创建这个标签时,我们拥有完全的访问权限。
该方法可以返回任意字符串,因此我们格式化这个字符串。如果我们不想显示标签,可以设置为null。
在下面的两个例子中很好的说明了这个特征。
6 实例一
本例实现一个当条目值大于某个限定值时就显示标签,效果如下图:
做到这一点并不困难,需要做以下工作:
l写一个实现接口CategoryItemLabelGenerator的类,并且实现generateItemLabel()方法。该方法实现如果条目的值小于限定值时,返回null。
l创建该类的实例,将该实例使用renderer的方法setLabelGenerator()设置到renderer中去。
代码如下:
/** * 演示自定义ItemLabel生成器 * Item值大于某限定值时显示标签 * @author mervin(马建新) * */ public class ItemLabelDemo1 extends ApplicationFrame { // 自定义的标签生成器 static class LabelGenerator extends AbstractCategoryItemLabelGenerator implements CategoryItemLabelGenerator { private double threshold; public LabelGenerator(double d) { super("", NumberFormat.getInstance()); threshold = d; } public String generateLabel(CategoryDataset categorydataset, int i, int i_0_) { String string = null; Number number = categorydataset.getValue(i, i_0_); if (number != null) { double d = number.doubleValue(); if (d > threshold) string = number.toString(); } return string; } } public ItemLabelDemo1(String string) { super(string); CategoryDataset categorydataset = createDataset(); JFreeChart jfreechart = createChart(categorydataset); ChartPanel chartpanel = new ChartPanel(jfreechart); chartpanel.setPreferredSize(new Dimension(500, 270)); setContentPane(chartpanel); } private static CategoryDataset createDataset() { DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset(); defaultcategorydataset.addValue(11.0, "S1", "C1"); defaultcategorydataset.addValue(44.3, "S1", "C2"); defaultcategorydataset.addValue(93.0, "S1", "C3"); defaultcategorydataset.addValue(35.6, "S1", "C4"); defaultcategorydataset.addValue(75.1, "S1", "C5"); return defaultcategorydataset; } // 创建图表 private static JFreeChart createChart(CategoryDataset categorydataset) { JFreeChart jfreechart = ChartFactory.createBarChart( "Item Label Demo 1", "Category", "Value", categorydataset, PlotOrientation.VERTICAL, false, true, false); jfreechart.setBackgroundPaint(Color.white); CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot(); categoryplot.setBackgroundPaint(Color.lightGray); categoryplot.setDomainGridlinePaint(Color.white); categoryplot.setRangeGridlinePaint(Color.white); CategoryItemRenderer renderer = categoryplot.getRenderer(); //设置ItemLabel生成器 renderer.setBaseItemLabelGenerator(new LabelGenerator(50.0D)); //设置显示ItemLabel renderer.setBaseItemLabelsVisible(true); NumberAxis numberaxis = (NumberAxis) categoryplot.getRangeAxis(); numberaxis.setUpperMargin(0.15); return jfreechart; } public static JPanel createDemoPanel() { JFreeChart jfreechart = createChart(createDataset()); return new ChartPanel(jfreechart); } public static void main(String[] strings) { ItemLabelDemo1 itemlabeldemo1 = new ItemLabelDemo1("Item Label Demo 1"); itemlabeldemo1.pack(); RefineryUtilities.centerFrameOnScreen(itemlabeldemo1); itemlabeldemo1.setVisible(true); } }
7 实例二
在本实例中,目的是在每个系列的标签上显示出值和百分比值(这个百分比值,这个系列在某一部分的条形直方图或全部条形直方图的总值中的比值)。如下图所示。
该实现中,标签产生器计算出百分比。如果传入构造函数的是一个种类索引,那么这个百分比的基数就是指定种类的当前系列的值。如果种类索引是无效的,那么这个基数就是指定种类的全部系列总和。
标签产生器会默认创建一个百分比格式——一种比较成熟的格式,提供格式化能力。
代码:
/** * 自定义Item标签生成器 * 生成百分比的ItemLabel * @author mervin(马建新) * */ public class ItemLabelDemo2 extends ApplicationFrame { /** * 自定义标签生成器 */ static class LabelGenerator extends AbstractCategoryItemLabelGenerator implements CategoryItemLabelGenerator { private Integer category; private NumberFormat formatter = NumberFormat.getPercentInstance(); public LabelGenerator(int i) { this(new Integer(i)); } public LabelGenerator(Integer integer) { super("", NumberFormat.getInstance()); category = integer; } public String generateLabel(CategoryDataset categorydataset, int i, int i_0_) { String string = null; double d = 0.0; if (category != null) { Number number = categorydataset.getValue(i, category.intValue()); d = number.doubleValue(); } else d = calculateSeriesTotal(categorydataset, i); Number number = categorydataset.getValue(i, i_0_); if (number != null) { double d_1_ = number.doubleValue(); string = (number.toString() + " (" + formatter.format(d_1_ / d) + ")"); } return string; } //计算总计 private double calculateSeriesTotal(CategoryDataset categorydataset,int i) { double d = 0.0; for (int i_2_ = 0; i_2_ < categorydataset.getColumnCount(); i_2_++) { Number number = categorydataset.getValue(i, i_2_); if (number != null) d += number.doubleValue(); } return d; } } public ItemLabelDemo2(String string) { super(string); CategoryDataset categorydataset = createDataset(); JFreeChart jfreechart = createChart(categorydataset); ChartPanel chartpanel = new ChartPanel(jfreechart); chartpanel.setPreferredSize(new Dimension(500, 270)); setContentPane(chartpanel); } private static CategoryDataset createDataset() { DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset(); defaultcategorydataset.addValue(100.0, "S1", "C1"); defaultcategorydataset.addValue(44.3, "S1", "C2"); defaultcategorydataset.addValue(93.0, "S1", "C3"); defaultcategorydataset.addValue(80.0, "S2", "C1"); defaultcategorydataset.addValue(75.1, "S2", "C2"); defaultcategorydataset.addValue(15.1, "S2", "C3"); return defaultcategorydataset; } private static JFreeChart createChart(CategoryDataset categorydataset) { JFreeChart jfreechart = ChartFactory.createBarChart( "Item Label Demo 2", "Category", "Value", categorydataset, PlotOrientation.HORIZONTAL, true, true, false); jfreechart.setBackgroundPaint(Color.white); CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot(); categoryplot.setBackgroundPaint(Color.lightGray); categoryplot.setDomainGridlinePaint(Color.white); categoryplot.setRangeGridlinePaint(Color.white); categoryplot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT); NumberAxis numberaxis = (NumberAxis) categoryplot.getRangeAxis(); numberaxis.setUpperMargin(0.25); CategoryItemRenderer categoryitemrenderer = categoryplot.getRenderer(); categoryitemrenderer.setBaseItemLabelsVisible(true); categoryitemrenderer.setBaseItemLabelGenerator(new LabelGenerator( (Integer) null)); return jfreechart; } public static JPanel createDemoPanel() { JFreeChart jfreechart = createChart(createDataset()); return new ChartPanel(jfreechart); } public static void main(String[] strings) { ItemLabelDemo2 itemlabeldemo2 = new ItemLabelDemo2("Item Label Demo 2"); itemlabeldemo2.pack(); RefineryUtilities.centerFrameOnScreen(itemlabeldemo2); itemlabeldemo2.setVisible(true); } }