Simple Pie Chart in Java Swing

Update !

This pie chart compoment has now been moved into the Iceberg Charts project . Please use Iceberg Charts for a more complete charting experience.

I know there are a lot of Swing charting software libraries out there (JFreechart comes to mind). But a lot of them come across as too bloated and require a lot of time to become familiar with.

My own Piechart is as simple as it gets; collect your segments as percentages in a list, along with the corresponding colours :

ArrayList<Double> values = new ArrayList<Double>();
values.add(new Double(10));
values.add(new Double(20));
values.add(new Double(30));
values.add(new Double(15));
values.add(new Double(15));

ArrayList<Color> colors = new ArrayList<Color>();
colors.add(Color.gray);
colors.add(Color.green);
colors.add(Color.blue);
colors.add(Color.pink);
colors.add(Color.yellow);
 PieChart pieChart = new PieChart(values, colors);
myPanel.add(pieChart);

You can get something that looks like this :

Its really basic, but its also very minimalist and does away with a lot of noise that other charting programs offer.

Simple Indicator.

I thought I would also add a couple more modes to my pie chart. The second mode is what I would call a simple indicator mode. This displays just one percentage value as a slice of the pie.

PIeChart pieChart = new PieChart(70);

I find these indicators look very effective as small graphics next to numerical text, as the following example tries to show:

I’m not 100% sure this is the best way to visually enhance percentages, but I think it looks good in certain situations.

Graded Indicator.

Another visual approach to showing a percentage growing into a critical phase, is to somehow grade the colors as the percentage level gets higher. I decided to create a third mode which allows the user to set a number of gradings with corresponding colors. As the percentage gets close to 100% the pie sections get smaller and the colors get more prominent.

Here is an example :

To use the third mode simply add an array of percentages with an array of colours, plus an overall percentage, to a third constructor :

        ArrayList<Double> values = new ArrayList<Double>();

        values.add(new Double(60));
        values.add(new Double(20));
        values.add(new Double(10));
        values.add(new Double(7));
        values.add(new Double(3));

        ArrayList<Color> colors = new ArrayList<Color>();

        colors.add(new Color(255,255,255));
        colors.add(new Color(255,183,183));
        colors.add(new Color(255,6,6));
        colors.add(new Color(244,255,43));
        colors.add(Color.BLUE);

        PieChart pc = new PieChart(grade, values, colors);

Here is the source code to the pie chart with all three modes :

 

package com.gg.piechart;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;

import javax.swing.JPanel;

public class PieChart extends JPanel {

	enum Type {
		STANDARD, SIMPLE_INDICATOR, GRADED_INDICATOR
	}

	private Type type = null; //the type of pie chart

        private ArrayList values;
        private ArrayList colors;
 
        private ArrayList gradingValues;
        private ArrayList gradingColors;

	double percent = 0; //percent is used for simple indicator and graded indicator

	public PieChart(int percent) {

		type = Type.SIMPLE_INDICATOR;
		this.percent = percent;
	}

	public PieChart(ArrayList values, ArrayList colors) {

		type = Type.STANDARD;

		this.values = values;
		this.colors = colors;
	}

	public PieChart(int percent, ArrayList gradingValues, ArrayList gradingColors) {
		type = Type.GRADED_INDICATOR;

		this.gradingValues = gradingValues;
		this.gradingColors = gradingColors;
		this.percent = percent;

	}

	@Override
	protected void paintComponent(Graphics g) {

		int width = getSize().width;

		Graphics2D g2d = (Graphics2D) g;
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias!
				RenderingHints.VALUE_ANTIALIAS_ON);

		if (type == Type.SIMPLE_INDICATOR) {

			//colours used for simple indicator
			Color backgroundColor = Color.WHITE;
			Color mainColor = Color.BLUE;

			g2d.setColor(backgroundColor);
			g2d.fillOval(0, 0, width, width);
			g2d.setColor(mainColor);
			Double angle = (percent / 100) * 360;
			g2d.fillArc(0, 0, width, width, -270, -angle.intValue());

		} else if (type == Type.STANDARD) {

			int lastPoint = -270;

			for (int i = 0; i < values.size(); i++) {
				g2d.setColor(colors.get(i));

				Double val = values.get(i);
				Double angle = (val / 100) * 360;

				g2d.fillArc(0, 0, width, width, lastPoint, -angle.intValue());
				System.out.println("fill arc " + lastPoint + " "
						+ -angle.intValue());

				lastPoint = lastPoint + -angle.intValue();
			}
		} else if (type == Type.GRADED_INDICATOR) {

			int lastPoint = -270;

			double gradingAccum = 0;

			for (int i = 0; i < gradingValues.size(); i++) { 				g2d.setColor(gradingColors.get(i)); 				 				Double val = gradingValues.get(i); 				gradingAccum = gradingAccum + val; 				 				Double angle = null; 				 				/** 				 * If the sum of the gradings is greater than the percent, then we want to recalculate 				 * the last wedge, and break out of drawing. 				 */ 				 				if (gradingAccum > percent) {

					System.out.println("gradingAccum > percent");

					//get the previous accumulated segments. Segments minus last one
					double gradingAccumMinusOneSegment = gradingAccum - val;

					//make an adjusted calculation of the last wedge
					angle = ((percent - gradingAccumMinusOneSegment) / 100) * 360;

					g2d.fillArc(0, 0, width, width, lastPoint, -angle.intValue());

					lastPoint = lastPoint + -angle.intValue();

					break;

				}else {

					System.out.println("normal");
					angle = (val / 100) * 360;

					g2d.fillArc(0, 0, width, width, lastPoint, -angle.intValue());

					System.out.println("fill arc " + lastPoint + " "
							+ -angle.intValue());

					lastPoint = lastPoint + -angle.intValue();
				}
			}
		}
	}
}

Posted in Swing | Tagged , , , | 11 Comments

11 Responses to Simple Pie Chart in Java Swing

  1. Ruwan Rathnayake says:

    Wow! Gotta say after searching and studying many Charting libraries, this is the easiest and the simplest one I’ve found. It’s so simple as even a newbie can use it really easily. And you have explained how to use it in a really clear way. Keep up the great work felixzacat!

  2. zlloyd1 says:

    I cannot get this code to work, because I keep getting errors about the lines,
    g2d.setColor(colors.get(i));
    Double val = values.get(i);
    saying, incompatible types,
    required: color
    found: object
    Am I missing something here??

    • felixzacat says:

      you should be passing in an array of Double values, and an array of Color objects. Maybe you are passing in the wrong object type?

      • Tiwaking says:

        Third time lucky. I’ve been trying to type in ArrayList*Double* (replace * with greater than/less than) and the code is automatically parsed and removed the greater than/less than signs. I think that is what has happened to the generics in your code example. There must be a greater-than/less-than escape character some where.
        Thanks for the pie chart!!

    • Tiwaking says:

      felixzacat is missing the Generic Type for the ArrayList

      // private ArrayList values;//felixzacat didnt generic this
      private ArrayListvalues;
      // private ArrayList colors;//felixzacat didnt generic this
      private ArrayListcolors;
      // private ArrayList gradingValues;//felixzacat didnt generic this
      private ArrayListgradingValues;
      // private ArrayList gradingColors;//felixzacat didnt generic this
      private ArrayListgradingColors;

      And here

      public PieChart(ArrayListvalues, ArrayListcolors) {

      And here

      public PieChart(int percent, ArrayListgradingValues, ArrayListgradingColors) {

  3. Philipp says:

    Nice job dude,
    saves me a lot of time.

    Just as a hint: You can use generics for the different ArrayList, like ArrayList.
    I get compile errors with the code above if i dont use generics or cast to the right Type.

    But really, thx a lot.

    • Tiwaking says:

      This is really strange: I typed in ArrayList and ArrayList with the code tags and it automatically removes and
      I think it has something to do with the code tags

  4. Simon Williams says:

    I used this and it’s pretty simple, I did have a slight problem where it wasn’t displaying. This display problem was due to my lack of Java knowledge where I did not set the size of the piechart.

    If you’re just fiddling don’t forget to do something like this to see it appear:
    pieChart.setSize(100, 100);

  5. Dave Smith says:

    Thanks for the code! I ported it over to SWT, here’s the SWT version (the main function is just there for testing)

    package com.gg.piechart;
    import java.util.ArrayList;

    import org.eclipse.swt.SWT;
    import org.eclipse.swt.events.PaintEvent;
    import org.eclipse.swt.events.PaintListener;
    import org.eclipse.swt.graphics.Color;
    import org.eclipse.swt.graphics.GC;
    import org.eclipse.swt.graphics.Point;
    import org.eclipse.swt.layout.FillLayout;
    import org.eclipse.swt.widgets.Canvas;
    import org.eclipse.swt.widgets.Composite;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Shell;

    public class PieChart extends Canvas
    {

    public PieChart(Composite parent, int style)
    {
    super(parent, style);
    type = Type.SIMPLE_INDICATOR;
    this.percent = 15.0;
    addPaintListener(new PaintListener()
    {
    public void paintControl(PaintEvent e) {
    paint(e);
    }
    });
    }

    enum Type {
    STANDARD, SIMPLE_INDICATOR, GRADED_INDICATOR
    }

    private Type type = null; //the type of pie chart

    private ArrayList values;
    private ArrayList colors;

    private ArrayList gradingValues;
    private ArrayList gradingColors;

    double percent = 0; //percent is used for simple indicator and graded indicator

    public void setPercent(int percent)
    {
    type = Type.SIMPLE_INDICATOR;
    this.percent = percent;
    }

    public void setValues(ArrayList values, ArrayList colors)
    {
    type = Type.STANDARD;

    this.values = values;
    this.colors = colors;
    }

    public void setPercentAndGrading(int percent, ArrayList gradingValues, ArrayList gradingColors)
    {
    type = Type.GRADED_INDICATOR;
    this.gradingValues = gradingValues;
    this.gradingColors = gradingColors;
    this.percent = percent;

    }

    protected void paint(PaintEvent pe)
    {
    Point size = getSize();
    int diameter = size.x > size.y ? size.y:size.x;

    GC gc = pe.gc;
    Display display = pe.display;
    gc.setAntialias(SWT.ON);

    if (type == Type.SIMPLE_INDICATOR)
    {
    //colors used for simple indicator
    Color backgroundColor = display.getSystemColor(SWT.COLOR_WHITE);//SWTResourceManager.getColor(SWT.COLOR_WHITE);
    Color mainColor = display.getSystemColor(SWT.COLOR_BLUE);//SWTResourceManager.getColor(SWT.COLOR_BLUE);
    gc.setBackground(backgroundColor);
    gc.fillOval(0, 0, diameter, diameter);
    gc.setBackground(mainColor);
    Double angle = (percent / 100) * 360;
    gc.fillArc(0, 0, diameter, diameter, -270, -angle.intValue());

    }
    else if (type == Type.STANDARD)
    {
    int lastPoint = -270;

    for (int i = 0; i < values.size(); i++)
    {
    gc.setForeground(colors.get(i));

    Double val = values.get(i);
    Double angle = (val / 100) * 360;

    gc.fillArc(0, 0, diameter, diameter, lastPoint, -angle.intValue());
    System.out.println("fill arc " + lastPoint + " "
    + -angle.intValue());

    lastPoint = lastPoint + -angle.intValue();
    }
    }
    else if (type == Type.GRADED_INDICATOR)
    {

    int lastPoint = -270;

    double gradingAccum = 0;

    for (int i = 0; i percent)
    {

    System.out.println(“gradingAccum > percent”);

    //get the previous accumulated segments. Segments minus last one
    double gradingAccumMinusOneSegment = gradingAccum – val;

    //make an adjusted calculation of the last wedge
    angle = ((percent – gradingAccumMinusOneSegment) / 100) * 360;

    gc.fillArc(0, 0, diameter, diameter, lastPoint, -angle.intValue());

    lastPoint = lastPoint + -angle.intValue();

    break;
    }
    else
    {

    System.out.println(“normal”);
    angle = (val / 100) * 360;

    gc.fillArc(0, 0, diameter, diameter, lastPoint, -angle.intValue());

    System.out.println(“fill arc ” + lastPoint + ” ”
    + -angle.intValue());

    lastPoint = lastPoint + -angle.intValue();
    }
    }
    }
    }

    public static void main(String[] args)
    {
    Display display = new Display();
    final Shell shell = new Shell(display, SWT.SHELL_TRIM);
    shell.setLayout(new FillLayout());
    PieChart chart = new PieChart(shell, SWT.NONE);
    chart.setPercent(25);
    shell.setSize(500, 500);
    shell.open();
    while (!shell.isDisposed()) {
    if (!display.readAndDispatch())
    display.sleep();
    }
    display.dispose();
    }
    }

  6. Keks says:

    Hey, thank you for your code.
    I was wondering if it is possible to use the piecharts in a loop. I tried it with an array of piecharts and it worked nearly right – they had all the same value at the end which is not right but while I debugged it every value was right. I hope you know what I mean and maybe you have an idea what I’m doing wrong?! Thank you in advance!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

THREE_COLUMN_PAGE