在jfreechart中,对于显示bar的控制,是通过对于series设置实现的,从而,在同一个series中,所有的bar的颜色都是一致的。这在一些需要更细控制的环境中,会存在一些问题。
为了实现在jfreechart中对于每一个bar的颜色显示控制,这里我选择XYBarchart作为例子,进行了相应的扩展。在jfreechart中,对于图形的控制,可以分为数据和绘制两个部分。在数据方面层次为:dataset->series->item;在绘制方面为:renderer->painter。基于这样的架构,这里扩展了XYInternalSeries、XYInternalDataItem、XYBarRenderer、XYBarPainter四个类,分别如下:
XYInternalSeries扩展类ExtendedXYInternalSeries,在其中定义了一个add方法,增加了一个Color的传入参数:
import java.awt.Color;
import org.jfree.data.xy.XYIntervalSeries;
public class ExtendedXYIntervalSeries extends XYIntervalSeries {
public ExtendedXYIntervalSeries(Comparable key) {
super(key);
// TODO Auto-generated constructor stub
}
public void add(double x, double xLow, double xHigh, double y, double yLow,
double yHigh,Color color) {
// TODO Auto-generated method stub
super.add(new ExtendedXYInternalDataItem(x,xLow,xHigh,y,yLow,yHigh,color), true);
}
/* (non-Javadoc)
* @see org.jfree.data.xy.XYIntervalSeries#add(double, double, double, double, double, double)
*/
@Override
public void add(double x, double xLow, double xHigh, double y, double yLow,
double yHigh) {
// TODO Auto-generated method stub
super.add(new ExtendedXYInternalDataItem(x,xLow,xHigh,y,yLow,yHigh), true);
}
XYInternalDataItem的扩展类ExtendedXyInternalDataItem,其中,增加了一个属性color,在jfreechart中,Item代表的是一个显示的单元,在BarChart中就是一个Bar,原来是没有color属性的,这里增加了这个属性,在后面的绘制时将被用到:
import java.awt.Color;
import org.jfree.data.xy.XYIntervalDataItem;
public class ExtendedXYInternalDataItem extends XYIntervalDataItem {
private Color color = null;
public ExtendedXYInternalDataItem(double x, double xLow, double xHigh, double y,
double yLow, double yHigh,Color color) {
super(x, xLow, xHigh, y, yLow, yHigh);
// TODO Auto-generated constructor stub
this.color = color;
}
/**
* @param x
* @param xLow
* @param xHigh
* @param y
* @param yLow
* @param yHigh
*/
public ExtendedXYInternalDataItem(double x, double xLow, double xHigh, double y,
double yLow, double yHigh) {
super(x, xLow, xHigh, y, yLow, yHigh);
// TODO Auto-generated constructor stub
}
/**
* @return the color
*/
public Color getColor() {
return color;
}
/**
* @param color the color to set
*/
public void setColor(Color color) {
this.color = color;
}
}
上面的扩展在数据方面,做好了准备,已经可以在每一个显示单元中设置一个颜色属性了,下面在绘制过程中,利用这个属性来控制bar的颜色。
在ExtendedBarRenderer中,重载drawItem函数,当bar有颜色定义时,调用扩展的BarPainter:
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.labels.XYItemLabelGenerator;
import org.jfree.chart.plot.CrosshairState;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRendererState;
import org.jfree.data.extend.xy.ExtendedXYInternalDataItem;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYIntervalSeriesCollection;
import org.jfree.ui.RectangleEdge;
public class ExtendedXYBarRenderer extends XYBarRenderer {
/**
*
*/
private static final long serialVersionUID = -2257416613878068969L;
/* (non-Javadoc)
* @see org.jfree.chart.renderer.xy.XYBarRenderer#drawItem(java.awt.Graphics2D, org.jfree.chart.renderer.xy.XYItemRendererState, java.awt.geom.Rectangle2D, org.jfree.chart.plot.PlotRenderingInfo, org.jfree.chart.plot.XYPlot, org.jfree.chart.axis.ValueAxis, org.jfree.chart.axis.ValueAxis, org.jfree.data.xy.XYDataset, int, int, org.jfree.chart.plot.CrosshairState, int)
*/
@Override
public void drawItem(Graphics2D g2, XYItemRendererState state,
Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot,
ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset,
int series, int item, CrosshairState crosshairState, int pass) {
// TODO Auto-generated method stub
if (!getItemVisible(series, item)) {
return;
}
XYIntervalSeriesCollection intervalDataset = (XYIntervalSeriesCollection) dataset;
ExtendedXYInternalDataItem myXYItem = (ExtendedXYInternalDataItem) intervalDataset.getSeries(series).getDataItem(item);
double value0;
double value1;
if (this.useYInterval) {
value0 = intervalDataset.getStartYValue(series, item);
value1 = intervalDataset.getEndYValue(series, item);
}
else {
value0 = this.base;
value1 = intervalDataset.getYValue(series, item);
}
if (Double.isNaN(value0) || Double.isNaN(value1)) {
return;
}
if (value0 <= value1) {
if (!rangeAxis.getRange().intersects(value0, value1)) {
return;
}
}
else {
if (!rangeAxis.getRange().intersects(value1, value0)) {
return;
}
}
double translatedValue0 = rangeAxis.valueToJava2D(value0, dataArea,
plot.getRangeAxisEdge());
double translatedValue1 = rangeAxis.valueToJava2D(value1, dataArea,
plot.getRangeAxisEdge());
double bottom = Math.min(translatedValue0, translatedValue1);
double top = Math.max(translatedValue0, translatedValue1);
double startX = intervalDataset.getStartXValue(series, item);
if (Double.isNaN(startX)) {
return;
}
double endX = intervalDataset.getEndXValue(series, item);
if (Double.isNaN(endX)) {
return;
}
if (startX <= endX) {
if (!domainAxis.getRange().intersects(startX, endX)) {
return;
}
}
else {
if (!domainAxis.getRange().intersects(endX, startX)) {
return;
}
}
// is there an alignment adjustment to be made?
if (this.barAlignmentFactor >= 0.0 && this.barAlignmentFactor <= 1.0) {
double x = intervalDataset.getXValue(series, item);
double interval = endX - startX;
startX = x - interval * this.barAlignmentFactor;
endX = startX + interval;
}
RectangleEdge location = plot.getDomainAxisEdge();
double translatedStartX = domainAxis.valueToJava2D(startX, dataArea,
location);
double translatedEndX = domainAxis.valueToJava2D(endX, dataArea,
location);
double translatedWidth = Math.max(1, Math.abs(translatedEndX
- translatedStartX));
double left = Math.min(translatedStartX, translatedEndX);
if (getMargin() > 0.0) {
double cut = translatedWidth * getMargin();
translatedWidth = translatedWidth - cut;
left = left + cut / 2;
}
Rectangle2D bar = null;
PlotOrientation orientation = plot.getOrientation();
if (orientation == PlotOrientation.HORIZONTAL) {
// clip left and right bounds to data area
bottom = Math.max(bottom, dataArea.getMinX());
top = Math.min(top, dataArea.getMaxX());
bar = new Rectangle2D.Double(
bottom, left, top - bottom, translatedWidth);
}
else if (orientation == PlotOrientation.VERTICAL) {
// clip top and bottom bounds to data area
bottom = Math.max(bottom, dataArea.getMinY());
top = Math.min(top, dataArea.getMaxY());
bar = new Rectangle2D.Double(left, bottom, translatedWidth,
top - bottom);
}
boolean positive = (value1 > 0.0);
boolean inverted = rangeAxis.isInverted();
RectangleEdge barBase;
if (orientation == PlotOrientation.HORIZONTAL) {
if (positive && inverted || !positive && !inverted) {
barBase = RectangleEdge.RIGHT;
}
else {
barBase = RectangleEdge.LEFT;
}
}
else {
if (positive && !inverted || !positive && inverted) {
barBase = RectangleEdge.BOTTOM;
}
else {
barBase = RectangleEdge.TOP;
}
}
if (getShadowsVisible()) {
this.barPainter.paintBarShadow(g2, this, series, item, bar, barBase,
!this.useYInterval);
}
if(myXYItem.getColor() == null)
{
this.barPainter.paintBar(g2, this, series, item, bar, barBase);
}
else
{
((ExtendedXYBarPainter)this.barPainter).paintBar(g2, this, series, item, bar, barBase,myXYItem.getColor());
}
if (isItemLabelVisible(series, item)) {
XYItemLabelGenerator generator = getItemLabelGenerator(series,
item);
drawItemLabel(g2, dataset, series, item, plot, generator, bar,
value1 < 0.0);
}
// update the crosshair point
double x1 = (startX + endX) / 2.0;
double y1 = dataset.getYValue(series, item);
double transX1 = domainAxis.valueToJava2D(x1, dataArea, location);
double transY1 = rangeAxis.valueToJava2D(y1, dataArea,
plot.getRangeAxisEdge());
int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
rangeAxisIndex, transX1, transY1, plot.getOrientation());
EntityCollection entities = state.getEntityCollection();
if (entities != null) {
addEntity(entities, bar, dataset, series, item, 0.0, 0.0);
}
}
}
在ExtendBarPainter中,实现对于每一个Bar的颜色设置:
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import org.jfree.chart.renderer.xy.GradientXYBarPainter;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.ui.RectangleEdge;
public class ExtendedXYBarPainter extends GradientXYBarPainter {
/**
*
*/
private static final long serialVersionUID = -7874550052210278757L;
XYBarRenderer myrenderer;
private double g1;
private double g2;
private double g3;
public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row,
int column, RectangularShape bar, RectangleEdge base, Color color) {
// TODO Auto-generated method stub
Paint itemPaint = renderer.getItemPaint(row, column);
Color c0, c1;
if (itemPaint instanceof Color) {
c0 = color;
c1 = c0.brighter();
}
else if (itemPaint instanceof GradientPaint) {
GradientPaint gp = (GradientPaint) itemPaint;
c0 = gp.getColor1();
c1 = gp.getColor2();
}
else {
c0 = Color.blue;
c1 = Color.blue.brighter();
}
// as a special case, if the bar colour has alpha == 0, we draw
// nothing.
if (c0.getAlpha() == 0) {
return;
}
if (base == RectangleEdge.TOP || base == RectangleEdge.BOTTOM) {
Rectangle2D[] regions = splitVerticalBar(bar, this.g1, this.g2,
this.g3);
GradientPaint gp = new GradientPaint((float) regions[0].getMinX(),
0.0f, c0, (float) regions[0].getMaxX(), 0.0f, Color.white);
g2.setPaint(gp);
g2.fill(regions[0]);
gp = new GradientPaint((float) regions[1].getMinX(), 0.0f,
Color.white, (float) regions[1].getMaxX(), 0.0f, c0);
g2.setPaint(gp);
g2.fill(regions[1]);
gp = new GradientPaint((float) regions[2].getMinX(), 0.0f, c0,
(float) regions[2].getMaxX(), 0.0f, c1);
g2.setPaint(gp);
g2.fill(regions[2]);
gp = new GradientPaint((float) regions[3].getMinX(), 0.0f, c1,
(float) regions[3].getMaxX(), 0.0f, c0);
g2.setPaint(gp);
g2.fill(regions[3]);
}
else if (base == RectangleEdge.LEFT || base == RectangleEdge.RIGHT) {
Rectangle2D[] regions = splitHorizontalBar(bar, this.g1, this.g2,
this.g3);
GradientPaint gp = new GradientPaint(0.0f,
(float) regions[0].getMinY(), c0, 0.0f,
(float) regions[0].getMaxX(), Color.white);
g2.setPaint(gp);
g2.fill(regions[0]);
gp = new GradientPaint(0.0f, (float) regions[1].getMinY(),
Color.white, 0.0f, (float) regions[1].getMaxY(), c0);
g2.setPaint(gp);
g2.fill(regions[1]);
gp = new GradientPaint(0.0f, (float) regions[2].getMinY(), c0,
0.0f, (float) regions[2].getMaxY(), c1);
g2.setPaint(gp);
g2.fill(regions[2]);
gp = new GradientPaint(0.0f, (float) regions[3].getMinY(), c1,
0.0f, (float) regions[3].getMaxY(), c0);
g2.setPaint(gp);
g2.fill(regions[3]);
}
// draw the outline...
if (renderer.isDrawBarOutline()) {
Stroke stroke = renderer.getItemOutlineStroke(row, column);
Paint paint = renderer.getItemOutlinePaint(row, column);
if (stroke != null && paint != null) {
g2.setStroke(stroke);
g2.setPaint(paint);
g2.draw(bar);
}
}
}
private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a,
double b, double c) {
Rectangle2D[] result = new Rectangle2D[4];
double x0 = bar.getMinX();
double x1 = Math.rint(x0 + (bar.getWidth() * a));
double x2 = Math.rint(x0 + (bar.getWidth() * b));
double x3 = Math.rint(x0 + (bar.getWidth() * c));
result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(), x1
- x0, bar.getHeight());
result[1] = new Rectangle2D.Double(x1, bar.getMinY(), x2 - x1, bar
.getHeight());
result[2] = new Rectangle2D.Double(x2, bar.getMinY(), x3 - x2, bar
.getHeight());
result[3] = new Rectangle2D.Double(x3, bar.getMinY(), bar.getMaxX()
- x3, bar.getHeight());
return result;
}
/**
* Splits a bar into subregions (elsewhere, these subregions will have
* different gradients applied to them).
*
* @param bar
* the bar shape.
* @param a
* the first division.
* @param b
* the second division.
* @param c
* the third division.
*
* @return An array containing four subregions.
*/
private Rectangle2D[] splitHorizontalBar(RectangularShape bar, double a,
double b, double c) {
Rectangle2D[] result = new Rectangle2D[4];
double y0 = bar.getMinY();
double y1 = Math.rint(y0 + (bar.getHeight() * a));
double y2 = Math.rint(y0 + (bar.getHeight() * b));
double y3 = Math.rint(y0 + (bar.getHeight() * c));
result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(), bar
.getWidth(), y1 - y0);
result[1] = new Rectangle2D.Double(bar.getMinX(), y1, bar.getWidth(),
y2 - y1);
result[2] = new Rectangle2D.Double(bar.getMinX(), y2, bar.getWidth(),
y3 - y2);
result[3] = new Rectangle2D.Double(bar.getMinX(), y3, bar.getWidth(),
bar.getMaxY() - y3);
return result;
}
}
最后利用XYBarChartDemo7,对于实现的的扩展测试如下:
import java.awt.Color;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.extend.ExtendedChartPanel;
import org.jfree.chart.extend.renderer.xy.ExtendedXYBarPainter;
import org.jfree.chart.extend.renderer.xy.ExtendedXYBarRenderer;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.extend.xy.ExtendedXYIntervalSeries;
import org.jfree.data.time.Day;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYIntervalSeries;
import org.jfree.data.xy.XYIntervalSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
@SuppressWarnings("serial")
public class XYBarChartDemo7 extends ApplicationFrame
{
public XYBarChartDemo7(String s)
{
super(s);
ChartPanel jpanel = (ChartPanel) createDemoPanel();
// JScrollPane scroll = new JScrollPane(jpanel);
// jpanel.setPreferredSize(new Dimension(2000, 1000));
setContentPane(jpanel);
jpanel.addChartMouseListener(new MyBarChartListener());
}
private static JFreeChart createChart(IntervalXYDataset intervalxydataset)
{
JFreeChart jfreechart = ChartFactory.createXYBarChart("XYBarChartDemo7", "Date", true, "Y", intervalxydataset, PlotOrientation.VERTICAL, true, true, false);
jfreechart.setBackgroundPaint(Color.white);
XYPlot xyplot = (XYPlot)jfreechart.getPlot();
xyplot.setDomainAxis(new DateAxis("Date"));
SymbolAxis symbolaxis = new SymbolAxis("Series", new String[] {
"S1", "S2", "S3"
});
symbolaxis.setGridBandsVisible(false);
xyplot.setRangeAxis(symbolaxis);
xyplot.setBackgroundPaint(Color.lightGray);
xyplot.setDomainGridlinePaint(Color.white);
xyplot.setRangeGridlinePaint(Color.white);
xyplot.setRenderer(new ExtendedXYBarRenderer());
XYBarRenderer xybarrenderer = (XYBarRenderer)xyplot.getRenderer();
xybarrenderer.setUseYInterval(true);
xybarrenderer.setShadowVisible(false);
xybarrenderer.setBarPainter(new ExtendedXYBarPainter());
return jfreechart;
}
private static IntervalXYDataset createDataset()
{
Day day = new Day(12, 6, 2007);
Day day1 = new Day(13, 6, 2007);
Day day2 = new Day(14, 6, 2007);
Day day3 = new Day(15, 6, 2007);
Day day4 = new Day(16, 6, 2007);
Day day5 = new Day(17, 6, 2007);
XYIntervalSeriesCollection xyintervalseriescollection = new XYIntervalSeriesCollection();
ExtendedXYIntervalSeries xyintervalseries = new ExtendedXYIntervalSeries("S1");
ExtendedXYIntervalSeries xyintervalseries1 = new ExtendedXYIntervalSeries("S2");
ExtendedXYIntervalSeries xyintervalseries2 = new ExtendedXYIntervalSeries("S3");
addItem(xyintervalseries, day, day1, 0);
xyintervalseries.add(day.getFirstMillisecond(), day.getFirstMillisecond(), day3.getLastMillisecond(), 0, -0.45, -0.35,Color.green);
addItem(xyintervalseries, day3, day3, 0);
addItem(xyintervalseries1, day, day5, 1);
addItem(xyintervalseries2, day2, day4, 2);
xyintervalseriescollection.addSeries(xyintervalseries);
xyintervalseriescollection.addSeries(xyintervalseries1);
xyintervalseriescollection.addSeries(xyintervalseries2);
return xyintervalseriescollection;
}
public static void addItem(XYIntervalSeries xyintervalseries, RegularTimePeriod regulartimeperiod, RegularTimePeriod regulartimeperiod1, int i)
{
xyintervalseries.add(regulartimeperiod.getFirstMillisecond(), regulartimeperiod.getFirstMillisecond(), regulartimeperiod1.getLastMillisecond(), i, (double)i - 0.10000000000000001D, (double)i + 0.45000000000000001D);
}
public static JPanel createDemoPanel()
{
return new ExtendedChartPanel(createChart(createDataset()));
}
public static void main(String args[])
{
/*
JFreeChart chart = createChart(createDataset());
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(600, 300);
shell.setLayout(new FillLayout());
shell.setText("Test for jfreechart running with SWT");
ChartComposite frame = new ChartComposite(shell, SWT.NONE, chart,
true);
// frame.pack();
frame.setHorizontalAxisTrace(true);
frame.setVerticalAxisTrace(true);
frame.addChartMouseListener(new MyBarChartListener());
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
*/
XYBarChartDemo7 xybarchartdemo7 = new XYBarChartDemo7("JFreeChart : XYBarChartDemo7.java");
xybarchartdemo7.pack();
RefineryUtilities.centerFrameOnScreen(xybarchartdemo7);
xybarchartdemo7.setVisible(true);
}
}
会看到,利用
xyintervalseries.add(day.getFirstMillisecond(), day.getFirstMillisecond(), day3.getLastMillisecond(), 0, -0.45, -0.35,Color.green);
设置的bar的颜色会按照不同的设置进行变化了。