JFreeChart之堆叠柱形图(StackedBar)
JFreeChart之堆叠柱形图(StackedBar)
最近的项目使用有个功能需要使用到堆叠柱形图,看了项目以前的代码实现没有想要的结果。所以自己就先到官网下载了 Demo,Demo里有个打包好的Jar包,直接运行看到效果,但是坑爹的是貌似没有对应的源码,所以只能根据class名称直接google了,所幸在github里找到对应的源码。
点我下载 访问密码 f62b
这是运行Demo找到想要实现的效果的大致图:
我最终想要实现的效果是这样的:
如果想要实现这个效果,可以使用
- new StandardCategoryItemLabelGenerator("{3}",
- new DecimalFormat("#0.0%")));
但是柱体的返回的值是10.1/50.1=20.2%
,40.1/50.1=79.8%
这不符合预期目标,所以就去看了一下源码,在StandardCategoryItemLabelGenerator
的父类AbstractCategoryItemLabelGenerator
中发现有createItemArray
这么一个方法:
可以发现柱体的标签值应该是由这个方法进行返回的,因此自己就对StandardCategoryItemLabelGenerator
进行了继承,并重写了这个方法。
在ExtendedStandardCategoryItemLabelGeneratory
中增加了isPercent
作为标签值是显示百分比还是仅仅格式化的判断参数,并重写了createItemArray
这个方法。
此时得到的效果如图:
然而,柱体的总值还是没有格式化。再看了一下ExtendedStackedBarRenderer
这个类的代码,找到了totalFormat
这个属性,将其值赋为new DecimalFormat("#0.0%")
。测试:
基本是想要的最终结果,下面是测试代码
ExtendedStackedBarRenderer
- package com.springapp.jfreechar;
- import java.awt.Color;
- import java.awt.Font;
- import java.awt.Graphics2D;
- import java.awt.geom.Rectangle2D;
- import java.text.DecimalFormat;
- import java.text.NumberFormat;
- import org.jfree.chart.axis.CategoryAxis;
- import org.jfree.chart.axis.ValueAxis;
- import org.jfree.chart.entity.CategoryItemEntity;
- import org.jfree.chart.entity.EntityCollection;
- import org.jfree.chart.labels.CategoryToolTipGenerator;
- import org.jfree.chart.plot.CategoryPlot;
- import org.jfree.chart.plot.PlotOrientation;
- import org.jfree.chart.renderer.category.CategoryItemRendererState;
- import org.jfree.chart.renderer.category.StackedBarRenderer;
- import org.jfree.data.category.CategoryDataset;
- import org.jfree.text.TextUtilities;
- import org.jfree.ui.TextAnchor;
- public class ExtendedStackedBarRenderer extends StackedBarRenderer {
- private static final long serialVersionUID = 1L;
- private boolean showPositiveTotal;
- private boolean showNegativeTotal;
- private Font totalLabelFont;
- private NumberFormat totalFormatter;
- public ExtendedStackedBarRenderer() {
- true;
- true;
- new Font("SansSerif", 1, 12);
- new DecimalFormat("#0.0%");
- /**
- * StackedBarRenderer 没有这个构造方法的,传入一个NumberFormat类,可以自定义实现每个柱体值显示格式
- * @param totalFormatter
- */
- public ExtendedStackedBarRenderer(NumberFormat totalFormatter) {
- true;
- true;
- new Font("SansSerif", 1, 12);
- this.totalFormatter = totalFormatter;
- public NumberFormat getTotalFormatter() {
- return totalFormatter;
- public void setTotalFormatter(NumberFormat numberformat) {
- if (numberformat == null) {
- throw new IllegalArgumentException("Null format not permitted.");
- else {
- return;
- public void drawItem(Graphics2D graphics2d, CategoryItemRendererState categoryitemrendererstate, Rectangle2D rectangle2d, CategoryPlot categoryplot, CategoryAxis categoryaxis,
- ValueAxis valueaxis, CategoryDataset categorydataset, int i, int j, int k) {
- if (number == null)
- return;
- double d = number.doubleValue();
- double d1 = categoryaxis.getCategoryMiddle(j, getColumnCount(), rectangle2d, categoryplot.getDomainAxisEdge()) - categoryitemrendererstate.getBarWidth() / 2D;
- double d2 = 0.0D;
- double d3 = 0.0D;
- for (int l = 0; l < i; l++) {
- if (number1 == null)
- continue;
- double d5 = number1.doubleValue();
- if (d5 > 0.0D)
- else
- double d4;
- double d6;
- if (d > 0.0D) {
- else {
- double d7 = Math.min(d4, d6);
- double d8 = Math.max(Math.abs(d6 - d4), getMinimumBarLength());
- null;
- if (plotorientation == PlotOrientation.HORIZONTAL)
- new Rectangle2D.Double(d7, d1, d8, categoryitemrendererstate.getBarWidth());
- else
- new Rectangle2D.Double(d1, d7, categoryitemrendererstate.getBarWidth(), d8);
- if (isDrawBarOutline() && categoryitemrendererstate.getBarWidth() > 3D) {
- if (categoryitemlabelgenerator != null && isItemLabelVisible(i, j))
- 0.0D);
- if (d > 0.0D) {
- if (showPositiveTotal && isLastPositiveItem(categorydataset, i, j)) {
- double d9 = calculateSumOfPositiveValuesForCategory(categorydataset, j);
- float f = (float) double1.getCenterX();
- float f2 = (float) double1.getMinY() - 4F;
- if (plotorientation == PlotOrientation.HORIZONTAL) {
- float) double1.getMaxX() + 4F;
- float) double1.getCenterY();
- 0.0D, TextAnchor.CENTER);
- else if (showNegativeTotal && isLastNegativeItem(categorydataset, i, j)) {
- double d10 = calculateSumOfNegativeValuesForCategory(categorydataset, j);
- float f1 = (float) double1.getCenterX();
- float f3 = (float) double1.getMaxY() + 4F;
- if (plotorientation == PlotOrientation.HORIZONTAL) {
- float) double1.getMinX() - 4F;
- float) double1.getCenterY();
- 0.0D, TextAnchor.CENTER);
- if (categoryitemrendererstate.getInfo() != null) {
- if (entitycollection != null) {
- null;
- if (categorytooltipgenerator != null)
- null;
- if (getItemURLGenerator(i, j) != null)
- new CategoryItemEntity(double1, s, s1, categorydataset, categorydataset.getRowKey(i), categorydataset.getColumnKey(j));
- private boolean isLastPositiveItem(CategoryDataset categorydataset, int i, int j) {
- boolean flag = true;
- if (number == null)
- return false;
- for (int k = i + 1; k < categorydataset.getRowCount(); k++) {
- if (number1 != null)
- 0.0D;
- return flag;
- private boolean isLastNegativeItem(CategoryDataset categorydataset, int i, int j) {
- boolean flag = true;
- if (number == null)
- return false;
- for (int k = i + 1; k < categorydataset.getRowCount(); k++) {
- if (number1 != null)
- 0.0D;
- return flag;
- private double calculateSumOfPositiveValuesForCategory(CategoryDataset categorydataset, int i) {
- double d = 0.0D;
- for (int j = 0; j < categorydataset.getRowCount(); j++) {
- if (number == null)
- continue;
- double d1 = number.doubleValue();
- if (d1 > 0.0D)
- return d;
- private double calculateSumOfNegativeValuesForCategory(CategoryDataset categorydataset, int i) {
- double d = 0.0D;
- for (int j = 0; j < categorydataset.getRowCount(); j++) {
- if (number == null)
- continue;
- double d1 = number.doubleValue();
- if (d1 < 0.0D)
- return d;
ExtendedStandardCategoryItemLabelGeneratory
- package com.springapp.jfreechar;
- import org.jfree.chart.HashUtilities;
- import org.jfree.chart.labels.AbstractCategoryItemLabelGenerator;
- import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
- import org.jfree.data.DataUtilities;
- import org.jfree.data.category.CategoryDataset;
- import org.jfree.util.ObjectUtilities;
- import org.jfree.util.PublicCloneable;
- import java.io.Serializable;
- import java.text.DateFormat;
- import java.text.MessageFormat;
- import java.text.NumberFormat;
- public class ExtendedStandardCategoryItemLabelGeneratory extends StandardCategoryItemLabelGenerator implements PublicCloneable, Cloneable, Serializable {
- private static final long serialVersionUID = -7108591260223293197L;
- private String labelFormat;
- private String nullValueString;
- private NumberFormat numberFormat;
- private DateFormat dateFormat;
- private NumberFormat percentFormat;
- //新增加了参数,柱体series的值是否是总值的百分比,还是仅仅将原来的小数转化为百分数
- private boolean isPercent = false;
- public ExtendedStandardCategoryItemLabelGeneratory(String labelFormat, NumberFormat formatter, NumberFormat percentFormatter,boolean isPercent){
- super(labelFormat, formatter, percentFormatter);
- if(labelFormat == null) {
- throw new IllegalArgumentException("Null \'labelFormat\' argument.");
- else if(formatter == null) {
- throw new IllegalArgumentException("Null \'formatter\' argument.");
- else if(percentFormatter == null) {
- throw new IllegalArgumentException("Null \'percentFormatter\' argument.");
- else {
- this.labelFormat = labelFormat;
- this.numberFormat = formatter;
- this.percentFormat = percentFormatter;
- this.dateFormat = null;
- this.nullValueString = "-";
- if (isPercent)
- this.isPercent = isPercent;
- @Override
- protected String generateLabelString(CategoryDataset dataset, int row, int column) {
- if(dataset == null) {
- throw new IllegalArgumentException("Null \'dataset\' argument.");
- else {
- null;
- this.createItemArray(dataset, row, column);
- this.labelFormat, items);
- return result;
- @Override
- protected Object[] createItemArray(CategoryDataset dataset, int row, int column) {
- new Object[]{dataset.getRowKey(row).toString(), dataset.getColumnKey(column).toString(), null, null};
- if(value != null) {
- if(this.numberFormat != null) {
- 2] = this.numberFormat.format(value);
- else if(this.dateFormat != null) {
- 2] = this.dateFormat.format(value);
- else {
- 2] = this.nullValueString;
- if(value != null) {
- double total = DataUtilities.calculateColumnTotal(dataset, column);
- //
- double percent = 0D;// / total;
- if (this.isPercent)
- //StandardCategoryItemLabelGenerator 返回的值是百分比
- else
- //返回自己原来的值
- //格式化
- 3] = this.percentFormat.format(percent);
- return result;
StackedBarChartDemo3
- package com.springapp.jfreechar;
- import java.awt.*;
- import java.text.DecimalFormat;
- import java.text.NumberFormat;
- import java.util.Iterator;
- import javax.swing.*;
- import com.sun.javafx.charts.Legend;
- import org.jfree.chart.*;
- import org.jfree.chart.axis.*;
- import org.jfree.chart.labels.*;
- import org.jfree.chart.plot.CategoryPlot;
- import org.jfree.chart.plot.PlotOrientation;
- import org.jfree.chart.title.LegendTitle;
- import org.jfree.data.category.CategoryDataset;
- import org.jfree.data.category.DefaultCategoryDataset;
- import org.jfree.ui.ApplicationFrame;
- import org.jfree.ui.RectangleEdge;
- import org.jfree.ui.RefineryUtilities;
- import org.jfree.ui.TextAnchor;
- // Referenced classes of package demo:
- // ExtendedStackedBarRenderer
- public class StackedBarChartDemo3 extends ApplicationFrame {
- private static final long serialVersionUID = 1L;
- public StackedBarChartDemo3(String s) {
- super(s);
- new Dimension(500, 270));
- private static CategoryDataset createDataset() {
- new DefaultCategoryDataset();
- .101D, "Series 1", "Cross Contamination (4.3)");
- .4D, "Series 2", "Cross Contamination (4.3)");
- return defaultcategorydataset;
- private static JFreeChart createChart(CategoryDataset categorydataset) {
- "Stacked Bar Chart Demo 3", "Category", "Value", categorydataset, PlotOrientation.VERTICAL, true, true, false);
- //柱体
- new ExtendedStackedBarRenderer(new DecimalFormat("#0.0%"));
- //柱体标签是否可见
- true);
- new Font("Arial", Font.PLAIN, 12);
- new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255), 0.0f, 0.0f, new Color(0, 0, 0)));
- //设置柱体标签值的格式
- new ExtendedStandardCategoryItemLabelGeneratory("{3}",
- new DecimalFormat("#0.0%"),false);
- //自定义柱体颜色
- new GradientPaint(0.0f, 0.0f, new Color(237, 125, 49), 0.0f, 0.0f, new Color(237, 125, 49));
- 0, p0);
- new GradientPaint(0.0f, 0.0f, new Color(91, 155, 213), 0.0f, 0.0f, new Color(91, 155, 213));
- 1, p1);
- //Y 轴
- //不设置是自动显示步长
- new NumberTickUnit(0.05));
- 0.14999999999999999D);
- 0.14999999999999999D);
- //设置是否是百分率
- //X 辆
- //x轴旋转
- 6.0));
- //标注
- 0, 0, 0, 0);
- return jfreechart;
- public static JPanel createDemoPanel() {
- return new ChartPanel(jfreechart);
- public static void main(String args[]) {
- new StackedBarChartDemo3("Stacked Bar Chart Demo 3");
- true);