JFreeChart之堆叠柱形图(StackedBar)
JAVA
JFreeChart
最近的项目使用有个功能需要使用到堆叠柱形图,看了项目以前的代码实现没有想要的结果。所以自己就先到官网下载了 Demo,Demo里有个打包好的Jar包,直接运行看到效果,但是坑爹的是貌似没有对应的源码,所以只能根据class名称直接google了,所幸在github里找到对应的源码。
点我下载 访问密码 f62b
这是运行Demo找到想要实现的效果的大致图:
我最终想要实现的效果是这样的:
如果想要实现这个效果,可以使用
- 1extendedstackedbarrenderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{3}",
- 2 NumberFormat.getPercentInstance(), new DecimalFormat("#0.0%")));
- 3
但是柱体的返回的值是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%")
。测试:
基本是想要的最终结果,下面是测试代码
- 1 <dependency>
- 2 <groupId>jfree</groupId>
- 3 <artifactId>jfreechart</artifactId>
- 4 <version>1.0.13</version>
- 5 </dependency>
ExtendedStackedBarRenderer
- 1package com.springapp.jfreechar;
- 2
- 3import java.awt.Color;
- 4import java.awt.Font;
- 5import java.awt.Graphics2D;
- 6import java.awt.geom.Rectangle2D;
- 7import java.text.DecimalFormat;
- 8import java.text.NumberFormat;
- 9
- 10import org.jfree.chart.axis.CategoryAxis;
- 11import org.jfree.chart.axis.ValueAxis;
- 12import org.jfree.chart.entity.CategoryItemEntity;
- 13import org.jfree.chart.entity.EntityCollection;
- 14import org.jfree.chart.labels.CategoryToolTipGenerator;
- 15import org.jfree.chart.plot.CategoryPlot;
- 16import org.jfree.chart.plot.PlotOrientation;
- 17import org.jfree.chart.renderer.category.CategoryItemRendererState;
- 18import org.jfree.chart.renderer.category.StackedBarRenderer;
- 19import org.jfree.data.category.CategoryDataset;
- 20import org.jfree.text.TextUtilities;
- 21import org.jfree.ui.TextAnchor;
- 22
- 23public class ExtendedStackedBarRenderer extends StackedBarRenderer {
- 24
- 25 private static final long serialVersionUID = 1L;
- 26 private boolean showPositiveTotal;
- 27 private boolean showNegativeTotal;
- 28 private Font totalLabelFont;
- 29 private NumberFormat totalFormatter;
- 30 public ExtendedStackedBarRenderer() {
- 31 showPositiveTotal = true;
- 32 showNegativeTotal = true;
- 33 totalLabelFont = new Font("SansSerif", 1, 12);
- 34 totalFormatter = new DecimalFormat("#0.0%");
- 35 }
- 36
- 37 /**
- 38 * StackedBarRenderer 没有这个构造方法的,传入一个NumberFormat类,可以自定义实现每个柱体值显示格式
- 39 * @param totalFormatter
- 40 */
- 41 public ExtendedStackedBarRenderer(NumberFormat totalFormatter) {
- 42 showPositiveTotal = true;
- 43 showNegativeTotal = true;
- 44 totalLabelFont = new Font("SansSerif", 1, 12);
- 45 this.totalFormatter = totalFormatter;
- 46 }
- 47
- 48 public NumberFormat getTotalFormatter() {
- 49 return totalFormatter;
- 50 }
- 51
- 52 public void setTotalFormatter(NumberFormat numberformat) {
- 53 if (numberformat == null) {
- 54 throw new IllegalArgumentException("Null format not permitted.");
- 55 } else {
- 56 totalFormatter = numberformat;
- 57 return;
- 58 }
- 59 }
- 60
- 61 public void drawItem(Graphics2D graphics2d, CategoryItemRendererState categoryitemrendererstate, Rectangle2D rectangle2d, CategoryPlot categoryplot, CategoryAxis categoryaxis,
- 62 ValueAxis valueaxis, CategoryDataset categorydataset, int i, int j, int k) {
- 63 Number number = categorydataset.getValue(i, j);
- 64 if (number == null)
- 65 return;
- 66 double d = number.doubleValue();
- 67 PlotOrientation plotorientation = categoryplot.getOrientation();
- 68 double d1 = categoryaxis.getCategoryMiddle(j, getColumnCount(), rectangle2d, categoryplot.getDomainAxisEdge()) - categoryitemrendererstate.getBarWidth() / 2D;
- 69 double d2 = 0.0D;
- 70 double d3 = 0.0D;
- 71 for (int l = 0; l < i; l++) {
- 72 Number number1 = categorydataset.getValue(l, j);
- 73 if (number1 == null)
- 74 continue;
- 75 double d5 = number1.doubleValue();
- 76 if (d5 > 0.0D)
- 77 d2 += d5;
- 78 else
- 79 d3 += d5;
- 80 }
- 81
- 82 org.jfree.ui.RectangleEdge rectangleedge = categoryplot.getRangeAxisEdge();
- 83 double d4;
- 84 double d6;
- 85 if (d > 0.0D) {
- 86 d4 = valueaxis.valueToJava2D(d2, rectangle2d, rectangleedge);
- 87 d6 = valueaxis.valueToJava2D(d2 + d, rectangle2d, rectangleedge);
- 88 } else {
- 89 d4 = valueaxis.valueToJava2D(d3, rectangle2d, rectangleedge);
- 90 d6 = valueaxis.valueToJava2D(d3 + d, rectangle2d, rectangleedge);
- 91 }
- 92 double d7 = Math.min(d4, d6);
- 93 double d8 = Math.max(Math.abs(d6 - d4), getMinimumBarLength());
- 94 Rectangle2D.Double double1 = null;
- 95 if (plotorientation == PlotOrientation.HORIZONTAL)
- 96 double1 = new Rectangle2D.Double(d7, d1, d8, categoryitemrendererstate.getBarWidth());
- 97 else
- 98 double1 = new Rectangle2D.Double(d1, d7, categoryitemrendererstate.getBarWidth(), d8);
- 99 java.awt.Paint paint = getItemPaint(i, j);
- 100 graphics2d.setPaint(paint);
- 101 graphics2d.fill(double1);
- 102 if (isDrawBarOutline() && categoryitemrendererstate.getBarWidth() > 3D) {
- 103 graphics2d.setStroke(getItemStroke(i, j));
- 104 graphics2d.setPaint(getItemOutlinePaint(i, j));
- 105 graphics2d.draw(double1);
- 106 }
- 107 org.jfree.chart.labels.CategoryItemLabelGenerator categoryitemlabelgenerator = getItemLabelGenerator(i, j);
- 108 if (categoryitemlabelgenerator != null && isItemLabelVisible(i, j))
- 109 drawItemLabel(graphics2d, categorydataset, i, j, categoryplot, categoryitemlabelgenerator, double1, d < 0.0D);
- 110 if (d > 0.0D) {
- 111 if (showPositiveTotal && isLastPositiveItem(categorydataset, i, j)) {
- 112 graphics2d.setPaint(Color.black);
- 113 graphics2d.setFont(totalLabelFont);
- 114 double d9 = calculateSumOfPositiveValuesForCategory(categorydataset, j);
- 115 float f = (float) double1.getCenterX();
- 116 float f2 = (float) double1.getMinY() - 4F;
- 117 TextAnchor textanchor = TextAnchor.BOTTOM_CENTER;
- 118 if (plotorientation == PlotOrientation.HORIZONTAL) {
- 119 f = (float) double1.getMaxX() + 4F;
- 120 f2 = (float) double1.getCenterY();
- 121 textanchor = TextAnchor.CENTER_LEFT;
- 122 }
- 123 TextUtilities.drawRotatedString(totalFormatter.format(d9), graphics2d, f, f2, textanchor, 0.0D, TextAnchor.CENTER);
- 124 }
- 125 } else if (showNegativeTotal && isLastNegativeItem(categorydataset, i, j)) {
- 126 graphics2d.setPaint(Color.black);
- 127 graphics2d.setFont(totalLabelFont);
- 128 double d10 = calculateSumOfNegativeValuesForCategory(categorydataset, j);
- 129 float f1 = (float) double1.getCenterX();
- 130 float f3 = (float) double1.getMaxY() + 4F;
- 131 TextAnchor textanchor1 = TextAnchor.TOP_CENTER;
- 132 if (plotorientation == PlotOrientation.HORIZONTAL) {
- 133 f1 = (float) double1.getMinX() - 4F;
- 134 f3 = (float) double1.getCenterY();
- 135 textanchor1 = TextAnchor.CENTER_RIGHT;
- 136 }
- 137 TextUtilities.drawRotatedString(totalFormatter.format(d10), graphics2d, f1, f3, textanchor1, 0.0D, TextAnchor.CENTER);
- 138 }
- 139 if (categoryitemrendererstate.getInfo() != null) {
- 140 EntityCollection entitycollection = categoryitemrendererstate.getEntityCollection();
- 141 if (entitycollection != null) {
- 142 String s = null;
- 143 CategoryToolTipGenerator categorytooltipgenerator = getToolTipGenerator(i, j);
- 144 if (categorytooltipgenerator != null)
- 145 s = categorytooltipgenerator.generateToolTip(categorydataset, i, j);
- 146 String s1 = null;
- 147 if (getItemURLGenerator(i, j) != null)
- 148 s1 = getItemURLGenerator(i, j).generateURL(categorydataset, i, j);
- 149 CategoryItemEntity categoryitementity = new CategoryItemEntity(double1, s, s1, categorydataset, categorydataset.getRowKey(i), categorydataset.getColumnKey(j));
- 150 entitycollection.add(categoryitementity);
- 151 }
- 152 }
- 153 }
- 154
- 155 private boolean isLastPositiveItem(CategoryDataset categorydataset, int i, int j) {
- 156 boolean flag = true;
- 157 Number number = categorydataset.getValue(i, j);
- 158 if (number == null)
- 159 return false;
- 160 for (int k = i + 1; k < categorydataset.getRowCount(); k++) {
- 161 Number number1 = categorydataset.getValue(k, j);
- 162 if (number1 != null)
- 163 flag = flag && number1.doubleValue() <= 0.0D;
- 164 }
- 165
- 166 return flag;
- 167 }
- 168
- 169 private boolean isLastNegativeItem(CategoryDataset categorydataset, int i, int j) {
- 170 boolean flag = true;
- 171 Number number = categorydataset.getValue(i, j);
- 172 if (number == null)
- 173 return false;
- 174 for (int k = i + 1; k < categorydataset.getRowCount(); k++) {
- 175 Number number1 = categorydataset.getValue(k, j);
- 176 if (number1 != null)
- 177 flag = flag && number1.doubleValue() >= 0.0D;
- 178 }
- 179
- 180 return flag;
- 181 }
- 182
- 183 private double calculateSumOfPositiveValuesForCategory(CategoryDataset categorydataset, int i) {
- 184 double d = 0.0D;
- 185 for (int j = 0; j < categorydataset.getRowCount(); j++) {
- 186 Number number = categorydataset.getValue(j, i);
- 187 if (number == null)
- 188 continue;
- 189 double d1 = number.doubleValue();
- 190 if (d1 > 0.0D)
- 191 d += d1;
- 192 }
- 193
- 194 return d;
- 195 }
- 196
- 197 private double calculateSumOfNegativeValuesForCategory(CategoryDataset categorydataset, int i) {
- 198 double d = 0.0D;
- 199 for (int j = 0; j < categorydataset.getRowCount(); j++) {
- 200 Number number = categorydataset.getValue(j, i);
- 201 if (number == null)
- 202 continue;
- 203 double d1 = number.doubleValue();
- 204 if (d1 < 0.0D)
- 205 d += d1;
- 206 }
- 207
- 208 return d;
- 209 }
- 210
- 211}
- 212
ExtendedStandardCategoryItemLabelGeneratory
- 1package com.springapp.jfreechar;
- 2
- 3import org.jfree.chart.HashUtilities;
- 4import org.jfree.chart.labels.AbstractCategoryItemLabelGenerator;
- 5import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
- 6import org.jfree.data.DataUtilities;
- 7import org.jfree.data.category.CategoryDataset;
- 8import org.jfree.util.ObjectUtilities;
- 9import org.jfree.util.PublicCloneable;
- 10
- 11import java.io.Serializable;
- 12import java.text.DateFormat;
- 13import java.text.MessageFormat;
- 14import java.text.NumberFormat;
- 15
- 16public class ExtendedStandardCategoryItemLabelGeneratory extends StandardCategoryItemLabelGenerator implements PublicCloneable, Cloneable, Serializable {
- 17
- 18 private static final long serialVersionUID = -7108591260223293197L;
- 19 private String labelFormat;
- 20 private String nullValueString;
- 21 private NumberFormat numberFormat;
- 22 private DateFormat dateFormat;
- 23 private NumberFormat percentFormat;
- 24 //新增加了参数,柱体series的值是否是总值的百分比,还是仅仅将原来的小数转化为百分数
- 25 private boolean isPercent = false;
- 26
- 27 public ExtendedStandardCategoryItemLabelGeneratory(String labelFormat, NumberFormat formatter, NumberFormat percentFormatter,boolean isPercent){
- 28 super(labelFormat, formatter, percentFormatter);
- 29 if(labelFormat == null) {
- 30 throw new IllegalArgumentException("Null \‘labelFormat\‘ argument.");
- 31 } else if(formatter == null) {
- 32 throw new IllegalArgumentException("Null \‘formatter\‘ argument.");
- 33 } else if(percentFormatter == null) {
- 34 throw new IllegalArgumentException("Null \‘percentFormatter\‘ argument.");
- 35 } else {
- 36 this.labelFormat = labelFormat;
- 37 this.numberFormat = formatter;
- 38 this.percentFormat = percentFormatter;
- 39 this.dateFormat = null;
- 40 this.nullValueString = "-";
- 41 if (isPercent)
- 42 this.isPercent = isPercent;
- 43 }
- 44
- 45 }
- 46
- 47 @Override
- 48 protected String generateLabelString(CategoryDataset dataset, int row, int column) {
- 49 if(dataset == null) {
- 50 throw new IllegalArgumentException("Null \‘dataset\‘ argument.");
- 51 } else {
- 52 String result = null;
- 53 Object[] items = this.createItemArray(dataset, row, column);
- 54 result = MessageFormat.format(this.labelFormat, items);
- 55 return result;
- 56 }
- 57 }
- 58 @Override
- 59 protected Object[] createItemArray(CategoryDataset dataset, int row, int column) {
- 60 Object[] result = new Object[]{dataset.getRowKey(row).toString(), dataset.getColumnKey(column).toString(), null, null};
- 61 Number value = dataset.getValue(row, column);
- 62 if(value != null) {
- 63 if(this.numberFormat != null) {
- 64 result[2] = this.numberFormat.format(value);
- 65 } else if(this.dateFormat != null) {
- 66 result[2] = this.dateFormat.format(value);
- 67 }
- 68 } else {
- 69 result[2] = this.nullValueString;
- 70 }
- 71
- 72 if(value != null) {
- 73 double total = DataUtilities.calculateColumnTotal(dataset, column);
- 74 //
- 75 double percent = 0D;// / total;
- 76 if (this.isPercent)
- 77 //StandardCategoryItemLabelGenerator 返回的值是百分比
- 78 percent = value.doubleValue() / total;
- 79 else
- 80 //返回自己原来的值
- 81 percent = value.doubleValue();
- 82 //格式化
- 83 result[3] = this.percentFormat.format(percent);
- 84 }
- 85
- 86 return result;
- 87 }
- 88
- 89
- 90}
- 91
StackedBarChartDemo3
- 1package com.springapp.jfreechar;
- 2
- 3import java.awt.*;
- 4import java.text.DecimalFormat;
- 5import java.text.NumberFormat;
- 6import java.util.Iterator;
- 7
- 8import javax.swing.*;
- 9
- 10import com.sun.javafx.charts.Legend;
- 11import org.jfree.chart.*;
- 12import org.jfree.chart.axis.*;
- 13import org.jfree.chart.labels.*;
- 14import org.jfree.chart.plot.CategoryPlot;
- 15import org.jfree.chart.plot.PlotOrientation;
- 16import org.jfree.chart.title.LegendTitle;
- 17import org.jfree.data.category.CategoryDataset;
- 18import org.jfree.data.category.DefaultCategoryDataset;
- 19import org.jfree.ui.ApplicationFrame;
- 20import org.jfree.ui.RectangleEdge;
- 21import org.jfree.ui.RefineryUtilities;
- 22import org.jfree.ui.TextAnchor;
- 23
- 24// Referenced classes of package demo:
- 25// ExtendedStackedBarRenderer
- 26
- 27public class StackedBarChartDemo3 extends ApplicationFrame {
- 28
- 29 private static final long serialVersionUID = 1L;
- 30
- 31 public StackedBarChartDemo3(String s) {
- 32 super(s);
- 33 JPanel jpanel = createDemoPanel();
- 34 jpanel.setPreferredSize(new Dimension(500, 270));
- 35 setContentPane(jpanel);
- 36 }
- 37
- 38 private static CategoryDataset createDataset() {
- 39 DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
- 40 defaultcategorydataset.addValue(.101D, "Series 1", "Cross Contamination (4.3)");
- 41 defaultcategorydataset.addValue(.4D, "Series 2", "Cross Contamination (4.3)");
- 42 return defaultcategorydataset;
- 43 }
- 44
- 45 private static JFreeChart createChart(CategoryDataset categorydataset) {
- 46
- 47 JFreeChart jfreechart = ChartFactory.createStackedBarChart("Stacked Bar Chart Demo 3", "Category", "Value", categorydataset, PlotOrientation.VERTICAL, true, true, false);
- 48
- 49 CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot();
- 50 //柱体
- 51 ExtendedStackedBarRenderer extendedstackedbarrenderer = new ExtendedStackedBarRenderer(new DecimalFormat("#0.0%"));
- 52 //柱体标签是否可见
- 53 extendedstackedbarrenderer.setBaseItemLabelsVisible(true);
- 54 Font labelFont = new Font("Arial", Font.PLAIN, 12);
- 55 extendedstackedbarrenderer.setBaseItemLabelPaint(new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255), 0.0f, 0.0f, new Color(0, 0, 0)));
- 56 //设置柱体标签值的格式
- 57 ExtendedStandardCategoryItemLabelGeneratory generator = new ExtendedStandardCategoryItemLabelGeneratory("{3}",
- 58 NumberFormat.getPercentInstance(), new DecimalFormat("#0.0%"),false);
- 59 extendedstackedbarrenderer.setBaseItemLabelGenerator(generator);
- 60 //自定义柱体颜色
- 61 Paint p0 = new GradientPaint(0.0f, 0.0f, new Color(237, 125, 49), 0.0f, 0.0f, new Color(237, 125, 49));
- 62 extendedstackedbarrenderer.setSeriesPaint(0, p0);
- 63 Paint p1 = new GradientPaint(0.0f, 0.0f, new Color(91, 155, 213), 0.0f, 0.0f, new Color(91, 155, 213));
- 64 extendedstackedbarrenderer.setSeriesPaint(1, p1);
- 65
- 66 categoryplot.setRenderer(extendedstackedbarrenderer);
- 67
- 68 //Y 轴
- 69 NumberAxis numberaxis = (NumberAxis) categoryplot.getRangeAxis();
- 70 //不设置是自动显示步长
- 71 numberaxis.setTickUnit(new NumberTickUnit(0.05));
- 72 numberaxis.setLowerMargin(0.14999999999999999D);
- 73 numberaxis.setUpperMargin(0.14999999999999999D);
- 74 //设置是否是百分率
- 75 numberaxis.setNumberFormatOverride(NumberFormat.getPercentInstance());
- 76
- 77 //X 辆
- 78 CategoryAxis categoryAxis = categoryplot.getDomainAxis();
- 79 //x轴旋转
- 80 categoryAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 6.0));
- 81
- 82 //标注
- 83 LegendTitle legendtitle = (LegendTitle)jfreechart.getLegend();
- 84 legendtitle.setBorder(0, 0, 0, 0);
- 85 return jfreechart;
- 86 }
- 87
- 88 public static JPanel createDemoPanel() {
- 89 JFreeChart jfreechart = createChart(createDataset());
- 90 return new ChartPanel(jfreechart);
- 91 }
- 92
- 93 public static void main(String args[]) {
- 94 StackedBarChartDemo3 stackedbarchartdemo3 = new StackedBarChartDemo3("Stacked Bar Chart Demo 3");
- 95 stackedbarchartdemo3.pack();
- 96 RefineryUtilities.centerFrameOnScreen(stackedbarchartdemo3);
- 97 stackedbarchartdemo3.setVisible(true);
- 98 }
- 99}
- 100
- 101