Workshop: Baking a Pie Graph

To draw this hour to a close, you will create PiePanel, a graphical user interface component that displays a pie graph. This component will be a subclass of JPanel, a simple Swing container that's useful as a place to draw something. One way to begin creating a class is to define the way objects of the class will be created. Programs that use the PiePanel class must undertake the following steps:

  • Create a PiePanel object by using the constructor method PiePanel(int). The integer specified as an argument is the number of slices the pie graph will contain.

  • Call the object's addSlice(Color, float) method to give a slice the designated color and value.

The value of each slice in PiePanel is the quantity represented by that slice. For example, Table 23.1 displays data about the status of student loan repayments in the U.S. from the start of the program in 1959 through November 1997, according to Congressional testimony by David A. Longanecker of the Office of Postsecondary Education.

Table 23.1. U.S. Student Loan Repayments

Amount repaid by students

$101 billion

Amount loaned to students still in school

$68 billion

Amount loaned to students making payments

$91 billion

Amount loaned to students who defaulted

$25 billion


You could use PiePanel to represent this data in a pie graph with the following statements:

PiePanel loans = new PiePanel(4);
loans.addSlice(Color.green, 101F);
loans.addSlice(Color.yellow, 68F);
loans.addSlice(Color.blue, 91F);
loans.addSlice(Color.red, 25F);


Screenshot shows the result in an applet that contains one component: a PiePanel created with the student loan data.

Screenshot Displaying student loan data on a pie graph.

[View full size image]Java ScreenShot


When a PiePanel object is created, the number of slices is specified in the constructor. You need to know three more things to be able to draw each slice:

  • The color of the slice, represented by a Color object
  • The value represented by each slice
  • The total value represented by all slices

A new helper class, PieSlice, will be used to represent each slice in the pie graph:

class PieSlice {
 Color color = Color.lightGray;
 float size = 0;
 PieSlice(Color pColor, float pSize) {
 color = pColor;
 size = pSize;
 }
}


Each slice is constructed by calling PieSlice(Color, float). The combined value of all slices will be stored as a private instance variable of the PiePanel class, totalSize. There also will be instance variables for the panel's background color (background) and a counter used to keep track of slices (current):

private int current = 0;
private float totalSize = 0;
private Color background;


Now that you have a PieSlice class to work with, you can create an array of PieSlice objects with another instance variable:

private PieSlice[] slice;


When a PiePanel object is created, none of the slices have been assigned a color or size. The only things that must be done in the constructor are defining the size of the slice array and saving the background color of the panel:

public PiePanel(int sliceCount) {
 slice = new PieSlice[sliceCount];
 background = getBackground();
}


The addSlice(Color, float) method is used to add a slice of the pie to the panel:

public void addSlice(Color sColor, float sSize) {
 if (current <= slice.length) {
 slice[current] = new PieSlice(sColor, sSize);
 totalSize += sSize;
 current++;
 }
}


The current instance variable is used to put each slice into its own element of the slice array. The length variable of an array contains the number of elements the array has been defined to hold, so as long as current is not larger than slice.length, you can continue adding slices to the panel. The PiePanel class handles all graphical operations in its paintComponent(Graphics) method, as you might expect. The trickiest thing about this task is drawing the arcs that represent each slice of the pie. This is handled in the following statements:

float start = 0;
for (int i = 0; i < slice.length; i++) {
 float extent = slice[i].size * 360F / totalSize;
 comp2D.setColor(slice[i].color);
 Arc2D.Float drawSlice = new Arc2D.Float(
 xInset, yInset, width, height, start, extent,
 Arc2D.Float.PIE);
 start += extent;
 comp2D.fill(drawSlice);
}


The start variable keeps track of where to start drawing an arc, and extent keeps track of the size of an arc. If you know the total size of all pie slices and the size of a specific slice, you can figure out extent by multiplying the arc's size by 360 and dividing that by the total of all slices. All of the arcs are drawn in a for loop: After each arc's extent is calculated, the arc is created and then extent is added to start. This causes each slice to begin right next to the last one. A call to the Graphics2D method fill() draws the arc. To bring all of this together, load your word processor and create a new file called PiePanel.java. Enter the full text of Listing 23.2, and then save and compile the file when you're done.

Listing 23.2. The Full Text of PiePanel.java
 1: import java.awt.*;
 2: import javax.swing.*;
 3: import java.awt.geom.*;
 4:
 5: public class PiePanel extends JPanel {
 6: private PieSlice[] slice;
 7: private int current = 0;
 8: private float totalSize = 0;
 9: private Color background;
10:
11: public PiePanel(int sliceCount) {
12: slice = new PieSlice[sliceCount];
13: background = getBackground();
14: }
15:
16: public void addSlice(Color sColor, float sSize) {
17: if (current <= slice.length) {
18: slice[current] = new PieSlice(sColor, sSize);
19: totalSize += sSize;
20: current++;
21: }
22: }
23:
24: public void paintComponent(Graphics comp) {
25: super.paintComponent(comp);
26: Graphics2D comp2D = (Graphics2D) comp;
27: int width = getSize().width - 10;
28: int height = getSize().height - 15;
29: int xInset = 5;
30: int yInset = 5;
31: if (width < 5) {
32: xInset = width;
33: }
34: if (height < 5) {
35: yInset = height;
36: }
37: comp2D.setColor(background);
38: comp2D.fillRect(0, 0, getSize().width, getSize().height);
39: comp2D.setColor(Color.lightGray);
40: Ellipse2D.Float pie = new Ellipse2D.Float(
41: xInset, yInset, width, height);
42: comp2D.fill(pie);
43: float start = 0;
44: for (int i = 0; i < slice.length; i++) {
45: float extent = slice[i].size * 360F / totalSize;
46: comp2D.setColor(slice[i].color);
47: Arc2D.Float drawSlice = new Arc2D.Float(
48: xInset, yInset, width, height, start, extent,
49: Arc2D.Float.PIE);
50: start += extent;
51: comp2D.fill(drawSlice);
52: }
53: }
54: }
55:
56: class PieSlice {
57: Color color = Color.lightGray;
58: float size = 0;
59:
60: PieSlice(Color pColor, float pSize) {
61: color = pColor;
62: size = pSize;
63: }
64: }


After you save and compile the file, you will have a PiePanel class that can be added as a component to any Java program's graphical user interface. You can't run the class directly, so to test PiePanel, you need to create a class that uses it. Listing 23.3 contains an applet that uses these panels, PieApplet.

Listing 23.3. The Full Text of PieApplet.java
 1: import java.awt.*;
 2: import javax.swing.*;
 3:
 4: public class PieApplet extends JApplet {
 5: Color uneasyBeingGreen = new Color(0xCC, 0xCC, 0x99);
 6: Color zuzusPetals = new Color(0xCC, 0x66, 0xFF);
 7: Color zootSuit = new Color(0x66, 0x66, 0x99);
 8: Color sweetHomeAvocado = new Color(0x66, 0x99, 0x66);
 9: Color shrinkingViolet = new Color(0x66, 0x66, 0x99);
10: Color miamiNice = new Color(0x33, 0xFF, 0xFF);
11: Color inBetweenGreen = new Color(0x00, 0x99, 0x66);
12: Color norwegianBlue = new Color(0x33, 0xCC, 0xCC);
13: Color purpleRain = new Color(0x66, 0x33, 0x99);
14: Color freckle = new Color (0x99, 0x66, 0x33);
15:
16: public void init() {
17: PiePanel pie = new PiePanel(10);
18: pie.addSlice(uneasyBeingGreen, 1306);
19: pie.addSlice(zuzusPetals, 1080);
20: pie.addSlice(zootSuit, 296);
21: pie.addSlice(sweetHomeAvocado, 242);
22: pie.addSlice(shrinkingViolet, 186);
23: pie.addSlice(miamiNice, 162);
24: pie.addSlice(inBetweenGreen, 144);
25: pie.addSlice(norwegianBlue, 143);
26: pie.addSlice(purpleRain, 129);
27: pie.addSlice(freckle, 127);
28: add(pie);
29: }
30: }


After compiling this applet, create a simple web page that contains it by entering Listing 23.4 and saving it as PieApplet.html.

Listing 23.4. The Full Text of PieApplet.html
1: <applet code="PieApplet.class" >
2: </applet>


The PieApplet applet displays a pie graph showing the population of the 10 most populated countries (in millions), using figures from a June 2005 U.S. Census International Data Base report. In order, they are China (1.306 billion), India (1.080 billion), the United States (296 million), Indonesia (242 million), Brazil (186 million), Pakistan (162 million), Bangladesh (144 million), Russia (143 million), Nigeria (129 million), and Japan (127 million). Because Java only has a few colors defined in the Color class, 10 new ones were created for use here and given descriptive names. The colors are expressed as hexadecimal values—in Java, hexadecimal numbers are preceded by 0x—but they could also have been specified as decimal values in each Color() constructor. Screenshot shows what this applet should look like in a browser such as Internet Explorer.

Screenshot Displaying population figures in a pie graph.

Java ScreenShot


When you are viewing the PieApplet program, resize the applet window—the pie graph will be redrawn with dimensions that match the new size. This occurs because the (x,y), height, and width variables of the arcs use variables that are based on the size of the PiePanel. When the applet window is resized, Java resizes the panel also. These variables are defined in Lines 26–33 of Listing 23.2 by calling the component's getSize() method, which returns a Dimension object that has height and width instance variables describing how big the component is.

By the Way

The names and hexadecimal values for the colors are from Hexadecimal Hues, a site devoted to assigning off-the-wall names to all the colors that can be used on the World Wide Web. To see more, visit the page http://tools.firmlist.com/hexagonalhues. You can find the current U.S. Census world population figures by visiting the web page http://www.census.gov/cgi-bin/ipc/idbrank.pl. If this page is unavailable, you can find similar information at the main Census page (http://www.census.gov) or the Population Research Bureau (http://www.prb.org).


      
Comments