前言
对于大多数的图表类型来说,JFreeChart允许我们在图表的每个条目上、 或者内部、 或者附
近显示条目标签。
本文主要讲述:
● 如何让条目标签可视(仅限于支持条目标签的图表类型)
● 如何改变条目标签的外观(字体和颜色)
● 如何指定条目标签的位置
● 如何定制条目标签的文本
已知的局限性
● 一些renderer不支持条目标签
● 轴范围的自动调节,忽略了条目标签的自动调整——如果图表的周围没有足够的空间(使用方法setUpperMargin()或setLowerMargin()进行了相应的调整),那么一些图表条目标签在图表上显示不出来
关于设置了标签不出现问题,我们需要从以下几个方面进行考虑:
● Renderere必须需要一个标签产生器——这是一个用来创建每一个标签的文本条目的对象。
● 一些renderer不支持条目标签(具体参考renderer相关的文档)
下面是正确的参考代码:
CategoryItemRenderer renderer = categoryplot.getRenderer();
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setBaseItemLabelsVisible(null); // clears the ALL series flag
renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, false);
一.显示条目标签(Displaying Item Labels)
条目标签默认是不显示
,因此我们需要使用renderer进行创建和显示条目标签。 这主要有以下两个步骤:
● 分配一个CategoryItemLabelGenerator或XYItemLabelGenerator给renderer—这是一个负责创建标签的对象。
● 在renderer里面设置一个标签可视的标志。 可以针对全部系列进行设置,也可以针对具体的每一个系列进行设置。
此外,我们可以定制条目标签的位置、 字体和颜色。 下问下将详细介绍
1.创建一个条目标签并赋值
使用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);
我们可以在标准产生器的构造函数中定制不同的行为。 当然了,我们也可以创建我们自定义的条目标签.(详情见四.自定义条目标签(Customising the Item Label Text)
)
注意: 这里需要提下关于StandardXYItemLabelGenerator是系统关于条目标签的一个实现类
StandardXYItemLabelGenerator extends AbstractXYItemLabelGenerator implements XYItemLabelGenerator
我们如果在自定义便签的时候同样也是需要extends AbstractXYItemLabelGenerator implements XYItemLabelGenerator
.
这里提下关于StandardXYItemLabelGenerator中字符串格式化说明: (具体查看实现可以知道)
{0}为SeriesKeyName
{1} 为x值
{2}为Y值
1.所有的系列显示条目标签
方法renderer.setBaseItemLabelsVisible(false)是控制着条目标签的显示。 对于CategoryItemRenderer:
CategoryItemRenderer renderer = categoryplot.getRenderer();
renderer.setBaseItemLabelsVisible(true);
同样对于:XYItemRenderer
XYItemRenderer renderer = categoryplot.getRenderer();
renderer.setBaseItemLabelsVisible(true);
一旦设置,这个标志优先管理我们在所有地方对每一系列做的设置,主要为了应用每一系列
的设置。 我们可以设置个标志为null,那么标签将不显示.
2.为选择的系列显示条目标签
下面代码可以设置如上效果:
CategoryItemRenderer renderer = categoryplot.getRenderer();
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setBaseItemLabelsVisible(null); // clears the ALL series flag
renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, false);
二.条目标签外观 (Item Label Appearance)
我们可以通过改变条目的颜色、 字体来改变图表条目标签的外观。 正如其他renderer属性一样,属性的设置可以是全部的系列(base),可以是具体某一系列(series)。
在JFreeChart目前的版本中,标签是一个透明的背景画出来的。 我们不能
设置标签的背景颜色
,也不能指定标签的边框
。
1.改变条目标签的字体(Changing the Label Font)
为了在所有的系列中改变条目标签的字体,我们可以使用下面的代码:
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)方法会出错。 开发指南显示的代码有错误
2.改变条目标签的颜色(Changing the Label Color)
改变条目标签的颜色,我们可以使用下面的代码:
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);方法会出错。 开发指南显示的代码有错误。
三.条目标签位置(Item Label Positioning)
条目标签的位置是通过ItemLabelPosition对象的四个属性来控制的。
我们可以通过接口CategoryItemRenderer的方法来独立定义条目标签的正负点位置:
public void setBasePositiveItemLabelPosition(ItemLabelPosition position);
public void setBaseNegativeItemLabelPosition(ItemLabelPosition position);
理解这些属性如何影响独立标签的最终位置的关键是了解JFreeChart里面条目标签的特征。
四个特征是:
● 条目标签点——决定标签的起始位置
● 文本点——标签里的文本相对于条目标签的位置。
● 旋转点——标签文本旋转的点位置
● 旋转角度——标签的旋转角度。
1.条目标签的位置
设置条目标签位置的目的,主要是为了找出标签在图表上贴向数据条目的一个点(x,y)位置。 同时在画图表时,该标签也被画在该点处。 更多的信息可以参考ItemLabelAnchor文档。
2.标签文本的位置
标签文本的位置,主要取决于上节讲的标签位置。 我们可以讲标签文本在标签里设置在右上部、 或左下部等,更多的信息参见TextAnchor文档。
3.标签旋转点
在标签上定义了一个旋转点,用于旋转标签。
4.标签旋转角度
旋转角度定义了标签沿旋转点旋转的角度。 该角度为弧度
四.自定义条目标签(Customising the Item Label Text)
定制条目标签文本,我们需要依赖用JFreeChart里的标签产生器来为条目标签创建文本。 如果要想完全控制标签文本的控制,我们就需要编写自己的标签产生器,需要实现接口CategoryItemLabelGenerator。
实现一个自定义的标签产生器(这里顺便提一下如果是自定义并转图标签需要实现的接口为PieSectionLabelGenerator)
开发一个自定义标签产生器,我们需要写一个类,该类必须实现CategoryItemLabelGenerator接口里的方法。public String generateLabel(CategoryDataset dataset, int series,int category)
该renderer调用该方法获得一个标签的字符串,并且将该字符串传入到当前条目的CategoryDataset、 序列和种类。 这就意味着创建这个标签时,我们拥有完全的访问权限。该方法可以返回任意字符串,因此我们格式化这个字符串。 如果我们不想显示标签,可以设置为null。
自定义条目标签生成器例子:在每个系列的标签上显示出值和百分比值(这个百分比值,这个系列在 某一部分的条形直方图或全部条形直方图的总值中的比值)
package com.yveshe.tutorial.customizer;
import java.text.NumberFormat;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.AbstractCategoryItemLabelGenerator;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.RefineryUtilities;
/**
* 用途: 目的是在每个系列的标签上显示出值和百分比值(这个百分比值,这个系列在 某一部分的条形直方图或全部条形直方图的总值中的比值)
*
* @author YvesHe
*
*/
public class ItemLabelDemo2 {
private static CategoryDataset createDataset() {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(100.0D, "S1", "C1");
dataset.addValue(44.299999999999997D, "S1", "C2");
dataset.addValue(93.0D, "S1", "C3");
dataset.addValue(80.0D, "S2", "C1");
dataset.addValue(75.099999999999994D, "S2", "C2");
dataset.addValue(15.1D, "S2", "C3");
return dataset;
}
private static JFreeChart createChart(CategoryDataset paramCategoryDataset) {
JFreeChart chart = ChartFactory.createBarChart("Item Label Demo -Yves", "Category", "Value", paramCategoryDataset, PlotOrientation.HORIZONTAL, true, true, false);
CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
plot.setRangePannable(true);
plot.setRangeZeroBaselineVisible(true);
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setUpperMargin(0.25D);
BarRenderer renderer = (BarRenderer) plot.getRenderer();
renderer.setBaseItemLabelsVisible(true);
renderer.setItemLabelAnchorOffset(7.0D);
renderer.setBaseItemLabelGenerator(new LabelGenerator(null));
// 标准的渲染器为长方条形图
renderer.setBarPainter(new StandardBarPainter());
return chart;
}
public static void main(String[] args) {
// create a chart...
JFreeChart chart = createChart(createDataset());
// create and display a frame...
ChartFrame frame = new ChartFrame("First", chart);
frame.pack();
RefineryUtilities.centerFrameOnScreen(frame);
frame.setVisible(true);
}
static class LabelGenerator extends AbstractCategoryItemLabelGenerator implements CategoryItemLabelGenerator {
private final Integer category;
private final NumberFormat formatter = NumberFormat.getPercentInstance();// 当前默认语言环境的百分比格式
public LabelGenerator(int paramInt) {
this(new Integer(paramInt));
}
public LabelGenerator(Integer paramInteger) {
super("", NumberFormat.getInstance());
this.category = paramInteger;
}
public String generateLabel(CategoryDataset dataset, int series, int category) {
String result = null;
double base = 0.0;
if (this.category != null) {
final Number b = dataset.getValue(series, this.category.intValue());
base = b.doubleValue();
} else {
base = calculateSeriesTotal(dataset, series);
}
Number value = dataset.getValue(series, category);
if (value != null) {
final double v = value.doubleValue();
// you could apply some formatting here
result = value.toString()
+ " (" + this.formatter.format(v / base) + ")";
}
return result;
}
/**
* Calculates a series total.
*
* @param dataset
* the dataset.
* @param series
* the series index.
*
* @return The total.
*/
private double calculateSeriesTotal(CategoryDataset dataset, int series) {
double d = 0.0D;
for (int i = 0; i < dataset.getColumnCount(); i++) {
Number value = dataset.getValue(series, i);
if (value != null)
d += value.doubleValue();
}
return d;
}
}
}