To draw this hour to a close, you create PiePanel
, a GUI component that displays a pie graph. This component is 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 are 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 contains.
• 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 United States for the first 38 years of the program, according to the Office of Postsecondary Education.
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);
Figure 23.2 shows the result in an application frame that contains one component: a PiePanel
created with the student loan data.
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
, is used to represent each slice in the pie graph:
import java.awt.*;
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 is stored as a private instance variable of the PiePanel
class, totalSize
. There also are 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 you create a PiePanel
object, none of the slices have an assigned a color or size. The only things that you must do in the constructor are define the size of the slice
array and save the background color of the panel:
public PiePanel(int sliceCount) {
slice = new PieSlice[sliceCount];
background = getBackground();
}
Use the addSlice(
Color,
float)
method 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; 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()
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 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 this together, create a new empty Java file named PiePanel
and enter into it the full text from Listing 23.1.
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: }
Listing 23.1 defines a PiePanel
class in lines 1–54 and a PieSlice
helper class in lines 56–64. The PiePanel
class can be used as a component in any Java program’s GUI. To test PiePanel
, you need to create a class that uses it.
Listing 23.2 contains an application that uses these panels, PieFrame
. Create a new empty Java file and enter the source code for this class from the listing.
1: import javax.swing.*;
2: import javax.swing.event.*;
3: import java.awt.*;
4:
5: public class PieFrame extends JFrame {
6: Color uneasyBeingGreen = new Color(0xCC, 0xCC, 0x99);
7: Color zuzusPetals = new Color(0xCC, 0x66, 0xFF);
8: Color zootSuit = new Color(0x66, 0x66, 0x99);
9: Color sweetHomeAvocado = new Color(0x66, 0x99, 0x66);
10: Color shrinkingViolet = new Color(0x66, 0x66, 0x99);
11: Color miamiNice = new Color(0x33, 0xFF, 0xFF);
12: Color inBetweenGreen = new Color(0x00, 0x99, 0x66);
13: Color norwegianBlue = new Color(0x33, 0xCC, 0xCC);
14: Color purpleRain = new Color(0x66, 0x33, 0x99);
15: Color freckle = new Color(0x99, 0x66, 0x33);
16:
17: public PieFrame() {
18: super("Pie Graph");
19: setLookAndFeel();
20: setSize(320, 290);
21: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
22: setVisible(true);
23:
24: PiePanel pie = new PiePanel(10);
25: pie.addSlice(uneasyBeingGreen, 1337);
26: pie.addSlice(zuzusPetals, 1189);
27: pie.addSlice(zootSuit, 311);
28: pie.addSlice(sweetHomeAvocado, 246);
29: pie.addSlice(shrinkingViolet, 203);
30: pie.addSlice(miamiNice, 187);
31: pie.addSlice(inBetweenGreen, 166);
32: pie.addSlice(norwegianBlue, 159);
33: pie.addSlice(purpleRain, 139);
34: pie.addSlice(freckle, 127);
35: add(pie);
36: }
37:
38: private void setLookAndFeel() {
39: try {
40: UIManager.setLookAndFeel(
41: "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
42: );
43: } catch (Exception exc) {
44: // ignore error
45: }
46: }
47:
48: public static void main(String[] arguments) {
49: PieFrame pf = new PieFrame();
50: }
51: }
The PieFrame
class is a simple graphical user interface that contains one component, a PiePanel
object created in line 24. The object’s addSlice()
method is called 10 times in lines 25–35 to add slices to the pie graph.
When you run the application, PieFrame
displays a pie graph showing the population of the 10 most populated countries (in millions), using figures from a July 2011 U.S. Census International Data Base report. In order, they are China (1.337 billion), India (1.189 billion), United States (311 million), Indonesia (246 million), Brazil (203 million), Pakistan (187 million), Nigeria (166 million), Bangladesh (159 million), Russia (139 million), and Japan (127 million).
Because Java only has a few colors defined in the Color
class, 10 new ones are 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 also could have been specified as decimal values in each Color()
constructor.
Figure 23.3 shows this application running.
By using fonts, colors and graphics, you can draw more attention to elements of your programs and make them more compelling for users.
Drawing something using the shapes available with Java might seem like more trouble than it’s worth. However, graphics depicted with polygons have two advantages over graphics that are loaded from image files:
• Speed—Even a small graphic, such as an icon, would take longer to load and display than a series of polygons.
• Scaling—You can change the size of an entire image that uses polygons simply by changing the values to create it. For example, you could add a function to the Sign
class that multiplies all (x,y) points in each shape by two before they are created, and it would result in an image twice as large. Polygon images scale much more quickly than image files and produce better results.
Q. How can I draw arcs that go clockwise rather than counterclockwise?
A. You can accomplish this by specifying the size of the arc as a negative number. The arc begins at the same point, but goes in the opposite direction in an elliptical path. For example, the following statement draws an open arc at (35,20) that is 90 degrees long, begins at the 0 degree mark, goes clockwise, and has a height of 20 and a width of 15:
Arc2D.Float smile = new Arc2D.Float(35F, 20F, 15F, 20F,
0F, -90F, Arc2D.Float.OPEN);
Q. Ellipses and circles don’t have corners. What are the (x,y) coordinates specified with the Ellipses.Float
constructor method?
A. The (x,y) coordinates represent the smallest x value and smallest y value of the oval or circle. If you drew an invisible rectangle around it, the upper-left corner of the rectangle would be the x and y coordinates used as arguments to the method.
Q. How can I use XRender with Java?
A. Java 7 adds support for drawing Java2D graphics with the XRender rendering engine in X11-based environments, typically on Linux. This functionality is off by default and must be turned on using a command-line option: Dsun.java2d.xrender=true
. XRender enables Java programs to employ the capabilities of modern graphics processing units (GPUs).
In NetBeans, you can set these options by choosing Run, Set Project Configuration, Customize. Use the VM Options field to set this option and click OK.
Q. Why do photographers ask you to say “cheese”?
A. The word cheese forces your mouth into a smile, as do the words “whiskey,” “breeze,” and “money.” Words that end with a long “e” generally cause the sides of your lips to curl upward and your teeth to show.
Another word that photographers sometimes use is “grin.” Though it doesn’t end in an “e,” it contorts the mouth and the meaning makes people smile.
Test whether your font and color skills are MAH-ve-lous by answering the following questions.
1. Which one of the following is not a constant used to select a color?
A. Color.cyan
B. Color.teal
C. Color.magenta
2. When you change the color of something and redraw it on a container, what must you do to make it visible?
A. Use the drawColor()
method.
B. Use the repaint()
statement.
C. Do nothing.
3. What do the initials RGB stand for?
A. Roy G. Biv
B. Red Green Blue
C. Lucy in the Sky with Diamonds
1. B. The primary color of the Jacksonville Jaguars, teal, has gone unrepresented in Color
.
2. B. The call to repaint()
causes the paintComponent()
method to be called manually.
3. B. If C. were the right answer, you could use colors that would only be visible years later during flashbacks.
To further explore the spectrum of possibilities when using fonts and color in your programs, do the following activities:
• Create a version of the PieFrame
class that takes color values and pie slice values as command-line arguments instead of including them in the source code of the application.
• Create an application that draws a stop sign on a panel using colors, shapes, and fonts.
To see Java programs that implement these activities, visit the book’s website at www.java24hours.com.
3.139.104.23