最近的项目使用有个功能需要使用到堆叠柱形图,看了项目以前的代码实现没有想要的结果。所以自己就先到官网下载了 Demo,Demo里有个打包好的Jar包,直接运行看到效果,但是坑爹的是貌似没有对应的源码,所以只能根据class名称直接google了,所幸在github里找到对应的源码。
点我下载 访问密码 f62b
这是运行Demo找到想要实现的效果的大致图:
我最终想要实现的效果是这样的:
如果想要实现这个效果,可以使用
-
extendedstackedbarrenderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{3}",
-
NumberFormat.getPercentInstance(), 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%")。测试:
基本是想要的最终结果,下面是测试代码
-
<dependency>
-
<groupId>jfree</groupId>
-
<artifactId>jfreechart</artifactId>
-
<version>1.0.13</version>
-
</dependency>
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() {
-
showPositiveTotal = true;
-
showNegativeTotal = true;
-
totalLabelFont = new Font("SansSerif", 1, 12);
-
totalFormatter = new DecimalFormat("#0.0%");
-
}
-
-
/**
-
* StackedBarRenderer 没有这个构造方法的,传入一个NumberFormat类,可以自定义实现每个柱体值显示格式
-
* @param totalFormatter
-
*/
-
public ExtendedStackedBarRenderer(NumberFormat totalFormatter) {
-
showPositiveTotal = true;
-
showNegativeTotal = true;
-
totalLabelFont = 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 {
-
totalFormatter = numberformat;
-
return;
-
}
-
}
-
-
public void drawItem(Graphics2D graphics2d, CategoryItemRendererState categoryitemrendererstate, Rectangle2D rectangle2d, CategoryPlot categoryplot, CategoryAxis categoryaxis,
-
ValueAxis valueaxis, CategoryDataset categorydataset, int i, int j, int k) {
-
Number number = categorydataset.getValue(i, j);
-
if (number == null)
-
return;
-
double d = number.doubleValue();
-
PlotOrientation plotorientation = categoryplot.getOrientation();
-
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++) {
-
Number number1 = categorydataset.getValue(l, j);
-
if (number1 == null)
-
continue;
-
double d5 = number1.doubleValue();
-
if (d5 > 0.0D)
-
d2 += d5;
-
else
-
d3 += d5;
-
}
-
-
org.jfree.ui.RectangleEdge rectangleedge = categoryplot.getRangeAxisEdge();
-
double d4;
-
double d6;
-
if (d > 0.0D) {
-
d4 = valueaxis.valueToJava2D(d2, rectangle2d, rectangleedge);
-
d6 = valueaxis.valueToJava2D(d2 + d, rectangle2d, rectangleedge);
-
} else {
-
d4 = valueaxis.valueToJava2D(d3, rectangle2d, rectangleedge);
-
d6 = valueaxis.valueToJava2D(d3 + d, rectangle2d, rectangleedge);
-
}
-
double d7 = Math.min(d4, d6);
-
double d8 = Math.max(Math.abs(d6 - d4), getMinimumBarLength());
-
Rectangle2D.Double double1 = null;
-
if (plotorientation == PlotOrientation.HORIZONTAL)
-
double1 = new Rectangle2D.Double(d7, d1, d8, categoryitemrendererstate.getBarWidth());
-
else
-
double1 = new Rectangle2D.Double(d1, d7, categoryitemrendererstate.getBarWidth(), d8);
-
java.awt.Paint paint = getItemPaint(i, j);
-
graphics2d.setPaint(paint);
-
graphics2d.fill(double1);
-
if (isDrawBarOutline() && categoryitemrendererstate.getBarWidth() > 3D) {
-
graphics2d.setStroke(getItemStroke(i, j));
-
graphics2d.setPaint(getItemOutlinePaint(i, j));
-
graphics2d.draw(double1);
-
}
-
org.jfree.chart.labels.CategoryItemLabelGenerator categoryitemlabelgenerator = getItemLabelGenerator(i, j);
-
if (categoryitemlabelgenerator != null && isItemLabelVisible(i, j))
-
drawItemLabel(graphics2d, categorydataset, i, j, categoryplot, categoryitemlabelgenerator, double1, d < 0.0D);
-
if (d > 0.0D) {
-
if (showPositiveTotal && isLastPositiveItem(categorydataset, i, j)) {
-
graphics2d.setPaint(Color.black);
-
graphics2d.setFont(totalLabelFont);
-
double d9 = calculateSumOfPositiveValuesForCategory(categorydataset, j);
-
float f = (float) double1.getCenterX();
-
float f2 = (float) double1.getMinY() - 4F;
-
TextAnchor textanchor = TextAnchor.BOTTOM_CENTER;
-
if (plotorientation == PlotOrientation.HORIZONTAL) {
-
f = (float) double1.getMaxX() + 4F;
-
f2 = (float) double1.getCenterY();
-
textanchor = TextAnchor.CENTER_LEFT;
-
}
-
TextUtilities.drawRotatedString(totalFormatter.format(d9), graphics2d, f, f2, textanchor, 0.0D, TextAnchor.CENTER);
-
}
-
} else if (showNegativeTotal && isLastNegativeItem(categorydataset, i, j)) {
-
graphics2d.setPaint(Color.black);
-
graphics2d.setFont(totalLabelFont);
-
double d10 = calculateSumOfNegativeValuesForCategory(categorydataset, j);
-
float f1 = (float) double1.getCenterX();
-
float f3 = (float) double1.getMaxY() + 4F;
-
TextAnchor textanchor1 = TextAnchor.TOP_CENTER;
-
if (plotorientation == PlotOrientation.HORIZONTAL) {
-
f1 = (float) double1.getMinX() - 4F;
-
f3 = (float) double1.getCenterY();
-
textanchor1 = TextAnchor.CENTER_RIGHT;
-
}
-
TextUtilities.drawRotatedString(totalFormatter.format(d10), graphics2d, f1, f3, textanchor1, 0.0D, TextAnchor.CENTER);
-
}
-
if (categoryitemrendererstate.getInfo() != null) {
-
EntityCollection entitycollection = categoryitemrendererstate.getEntityCollection();
-
if (entitycollection != null) {
-
String s = null;
-
CategoryToolTipGenerator categorytooltipgenerator = getToolTipGenerator(i, j);
-
if (categorytooltipgenerator != null)
-
s = categorytooltipgenerator.generateToolTip(categorydataset, i, j);
-
String s1 = null;
-
if (getItemURLGenerator(i, j) != null)
-
s1 = getItemURLGenerator(i, j).generateURL(categorydataset, i, j);
-
CategoryItemEntity categoryitementity = new CategoryItemEntity(double1, s, s1, categorydataset, categorydataset.getRowKey(i), categorydataset.getColumnKey(j));
-
entitycollection.add(categoryitementity);
-
}
-
}
-
}
-
-
private boolean isLastPositiveItem(CategoryDataset categorydataset, int i, int j) {
-
boolean flag = true;
-
Number number = categorydataset.getValue(i, j);
-
if (number == null)
-
return false;
-
for (int k = i + 1; k < categorydataset.getRowCount(); k++) {
-
Number number1 = categorydataset.getValue(k, j);
-
if (number1 != null)
-
flag = flag && number1.doubleValue() <= 0.0D;
-
}
-
-
return flag;
-
}
-
-
private boolean isLastNegativeItem(CategoryDataset categorydataset, int i, int j) {
-
boolean flag = true;
-
Number number = categorydataset.getValue(i, j);
-
if (number == null)
-
return false;
-
for (int k = i + 1; k < categorydataset.getRowCount(); k++) {
-
Number number1 = categorydataset.getValue(k, j);
-
if (number1 != null)
-
flag = flag && number1.doubleValue() >= 0.0D;
-
}
-
-
return flag;
-
}
-
-
private double calculateSumOfPositiveValuesForCategory(CategoryDataset categorydataset, int i) {
-
double d = 0.0D;
-
for (int j = 0; j < categorydataset.getRowCount(); j++) {
-
Number number = categorydataset.getValue(j, i);
-
if (number == null)
-
continue;
-
double d1 = number.doubleValue();
-
if (d1 > 0.0D)
-
d += d1;
-
}
-
-
return d;
-
}
-
-
private double calculateSumOfNegativeValuesForCategory(CategoryDataset categorydataset, int i) {
-
double d = 0.0D;
-
for (int j = 0; j < categorydataset.getRowCount(); j++) {
-
Number number = categorydataset.getValue(j, i);
-
if (number == null)
-
continue;
-
double d1 = number.doubleValue();
-
if (d1 < 0.0D)
-
d += d1;
-
}
-
-
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 {
-
String result = null;
-
Object[] items = this.createItemArray(dataset, row, column);
-
result = MessageFormat.format(this.labelFormat, items);
-
return result;
-
}
-
}
-
@Override
-
protected Object[] createItemArray(CategoryDataset dataset, int row, int column) {
-
Object[] result = new Object[]{dataset.getRowKey(row).toString(), dataset.getColumnKey(column).toString(), null, null};
-
Number value = dataset.getValue(row, column);
-
if(value != null) {
-
if(this.numberFormat != null) {
-
result[2] = this.numberFormat.format(value);
-
} else if(this.dateFormat != null) {
-
result[2] = this.dateFormat.format(value);
-
}
-
} else {
-
result[2] = this.nullValueString;
-
}
-
-
if(value != null) {
-
double total = DataUtilities.calculateColumnTotal(dataset, column);
-
//
-
double percent = 0D;// / total;
-
if (this.isPercent)
-
//StandardCategoryItemLabelGenerator 返回的值是百分比
-
percent = value.doubleValue() / total;
-
else
-
//返回自己原来的值
-
percent = value.doubleValue();
-
//格式化
-
result[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);
-
JPanel jpanel = createDemoPanel();
-
jpanel.setPreferredSize(new Dimension(500, 270));
-
setContentPane(jpanel);
-
}
-
-
private static CategoryDataset createDataset() {
-
DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
-
defaultcategorydataset.addValue(.101D, "Series 1", "Cross Contamination (4.3)");
-
defaultcategorydataset.addValue(.4D, "Series 2", "Cross Contamination (4.3)");
-
return defaultcategorydataset;
-
}
-
-
private static JFreeChart createChart(CategoryDataset categorydataset) {
-
-
JFreeChart jfreechart = ChartFactory.createStackedBarChart("Stacked Bar Chart Demo 3", "Category", "Value", categorydataset, PlotOrientation.VERTICAL, true, true, false);
-
-
CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot();
-
//柱体
-
ExtendedStackedBarRenderer extendedstackedbarrenderer = new ExtendedStackedBarRenderer(new DecimalFormat("#0.0%"));
-
//柱体标签是否可见
-
extendedstackedbarrenderer.setBaseItemLabelsVisible(true);
-
Font labelFont = new Font("Arial", Font.PLAIN, 12);
-
extendedstackedbarrenderer.setBaseItemLabelPaint(new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255), 0.0f, 0.0f, new Color(0, 0, 0)));
-
//设置柱体标签值的格式
-
ExtendedStandardCategoryItemLabelGeneratory generator = new ExtendedStandardCategoryItemLabelGeneratory("{3}",
-
NumberFormat.getPercentInstance(), new DecimalFormat("#0.0%"),false);
-
extendedstackedbarrenderer.setBaseItemLabelGenerator(generator);
-
//自定义柱体颜色
-
Paint p0 = new GradientPaint(0.0f, 0.0f, new Color(237, 125, 49), 0.0f, 0.0f, new Color(237, 125, 49));
-
extendedstackedbarrenderer.setSeriesPaint(0, p0);
-
Paint p1 = new GradientPaint(0.0f, 0.0f, new Color(91, 155, 213), 0.0f, 0.0f, new Color(91, 155, 213));
-
extendedstackedbarrenderer.setSeriesPaint(1, p1);
-
-
categoryplot.setRenderer(extendedstackedbarrenderer);
-
-
//Y 轴
-
NumberAxis numberaxis = (NumberAxis) categoryplot.getRangeAxis();
-
//不设置是自动显示步长
-
numberaxis.setTickUnit(new NumberTickUnit(0.05));
-
numberaxis.setLowerMargin(0.14999999999999999D);
-
numberaxis.setUpperMargin(0.14999999999999999D);
-
//设置是否是百分率
-
numberaxis.setNumberFormatOverride(NumberFormat.getPercentInstance());
-
-
//X 辆
-
CategoryAxis categoryAxis = categoryplot.getDomainAxis();
-
//x轴旋转
-
categoryAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 6.0));
-
-
//标注
-
LegendTitle legendtitle = (LegendTitle)jfreechart.getLegend();
-
legendtitle.setBorder(0, 0, 0, 0);
-
return jfreechart;
-
}
-
-
public static JPanel createDemoPanel() {
-
JFreeChart jfreechart = createChart(createDataset());
-
return new ChartPanel(jfreechart);
-
}
-
-
public static void main(String args[]) {
-
StackedBarChartDemo3 stackedbarchartdemo3 = new StackedBarChartDemo3("Stacked Bar Chart Demo 3");
-
stackedbarchartdemo3.pack();
-
RefineryUtilities.centerFrameOnScreen(stackedbarchartdemo3);
-
stackedbarchartdemo3.setVisible(true);
-
}
-
}
-
-