JFreeChart| 图表高级自定义-图表条目标签(Item Label)

前言

对于大多数的图表类型来说,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;
        }
    }

}

猜你喜欢

转载自blog.csdn.net/u011479200/article/details/81515620