JFreeChart之堆叠柱形图(StackedBar)

JFreeChart之堆叠柱形图(StackedBar)

最近的项目使用有个功能需要使用到堆叠柱形图,看了项目以前的代码实现没有想要的结果。所以自己就先到官网下载了 Demo,Demo里有个打包好的Jar包,直接运行看到效果,但是坑爹的是貌似没有对应的源码,所以只能根据class名称直接google了,所幸在github里找到对应的源码。
点我下载 访问密码 f62b
这是运行Demo找到想要实现的效果的大致图:
enter description here
我最终想要实现的效果是这样的:
enter description here
如果想要实现这个效果,可以使用

  1. extendedstackedbarrenderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{3}", 
  2. NumberFormat.getPercentInstance(), new DecimalFormat("#0.0%"))); 
  3.  

但是柱体的返回的值是10.1/50.1=20.2%,40.1/50.1=79.8%
enter description here
这不符合预期目标,所以就去看了一下源码,在StandardCategoryItemLabelGenerator的父类AbstractCategoryItemLabelGenerator中发现有createItemArray这么一个方法:
enter description here
可以发现柱体的标签值应该是由这个方法进行返回的,因此自己就对StandardCategoryItemLabelGenerator进行了继承,并重写了这个方法。
在ExtendedStandardCategoryItemLabelGeneratory中增加了isPercent作为标签值是显示百分比还是仅仅格式化的判断参数,并重写了createItemArray这个方法。
enter description here
此时得到的效果如图:
enter description here
然而,柱体的总值还是没有格式化。再看了一下ExtendedStackedBarRenderer这个类的代码,找到了totalFormat这个属性,将其值赋为new DecimalFormat("#0.0%")。测试:
enter description here
基本是想要的最终结果,下面是测试代码

  1. <dependency> 
  2. <groupId>jfree</groupId> 
  3. <artifactId>jfreechart</artifactId> 
  4. <version>1.0.13</version> 
  5. </dependency> 

ExtendedStackedBarRenderer

  1. package com.springapp.jfreechar; 
  2.  
  3. import java.awt.Color; 
  4. import java.awt.Font; 
  5. import java.awt.Graphics2D; 
  6. import java.awt.geom.Rectangle2D; 
  7. import java.text.DecimalFormat; 
  8. import java.text.NumberFormat; 
  9.  
  10. import org.jfree.chart.axis.CategoryAxis; 
  11. import org.jfree.chart.axis.ValueAxis; 
  12. import org.jfree.chart.entity.CategoryItemEntity; 
  13. import org.jfree.chart.entity.EntityCollection; 
  14. import org.jfree.chart.labels.CategoryToolTipGenerator; 
  15. import org.jfree.chart.plot.CategoryPlot; 
  16. import org.jfree.chart.plot.PlotOrientation; 
  17. import org.jfree.chart.renderer.category.CategoryItemRendererState; 
  18. import org.jfree.chart.renderer.category.StackedBarRenderer; 
  19. import org.jfree.data.category.CategoryDataset; 
  20. import org.jfree.text.TextUtilities; 
  21. import org.jfree.ui.TextAnchor; 
  22.  
  23. public 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

  1. package com.springapp.jfreechar; 
  2.  
  3. import org.jfree.chart.HashUtilities; 
  4. import org.jfree.chart.labels.AbstractCategoryItemLabelGenerator; 
  5. import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; 
  6. import org.jfree.data.DataUtilities; 
  7. import org.jfree.data.category.CategoryDataset; 
  8. import org.jfree.util.ObjectUtilities; 
  9. import org.jfree.util.PublicCloneable; 
  10.  
  11. import java.io.Serializable; 
  12. import java.text.DateFormat; 
  13. import java.text.MessageFormat; 
  14. import java.text.NumberFormat; 
  15.  
  16. public 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

  1. package com.springapp.jfreechar; 
  2.  
  3. import java.awt.*; 
  4. import java.text.DecimalFormat; 
  5. import java.text.NumberFormat; 
  6. import java.util.Iterator; 
  7.  
  8. import javax.swing.*; 
  9.  
  10. import com.sun.javafx.charts.Legend; 
  11. import org.jfree.chart.*; 
  12. import org.jfree.chart.axis.*; 
  13. import org.jfree.chart.labels.*; 
  14. import org.jfree.chart.plot.CategoryPlot; 
  15. import org.jfree.chart.plot.PlotOrientation; 
  16. import org.jfree.chart.title.LegendTitle; 
  17. import org.jfree.data.category.CategoryDataset; 
  18. import org.jfree.data.category.DefaultCategoryDataset; 
  19. import org.jfree.ui.ApplicationFrame; 
  20. import org.jfree.ui.RectangleEdge; 
  21. import org.jfree.ui.RefineryUtilities; 
  22. import org.jfree.ui.TextAnchor; 
  23.  
  24. // Referenced classes of package demo: 
  25. // ExtendedStackedBarRenderer 
  26.  
  27. public 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.  
posted @ 2016-03-09 22:00  无名的风吹草地  阅读(5953)  评论(0)    收藏  举报