Day 9

Graphics, Fonts, and Color

by Laura Lemay


CONTENTS

Knowing the basics of how applets work is only the first step. The next step is to become familiar with the capabilities Java gives you for drawing to the screen, performing dynamic updating, managing mouse and keyboard events, and creating user interface elements. You'll do all these things this week. You'll start today with how to draw to the screen-that is, how to produce lines and shapes with the built-in graphics primitives, how to print text using fonts, and how to use and modify color in your applets. Today you'll learn, specifically, the following:

Note
Today and for the rest of this week, you'll get an introduction to many of the classes that make up the Java class libraries, in particular the classes in the java.awt package. Keep in mind, however, that I only have the space to give you an introduction to these classes-there are many other capabilities available to you in these classes that you can use in your own programs, depending on what you're trying to accomplish. After you finish this book (and perhaps after each of these lessons), you'll want to familiarize yourself with the classes themselves and what they can do. Be sure to check out the Java API documentation for more details; you can find that API documentation on the Java Web site at http://java.sun.com/products/JDK/1.0.2/api/packages.html.

The Graphics Class

With the basic graphics capabilities built into Java's class libraries, you can draw lines, shapes, characters, and images to the screen inside your applet. Most of the graphics operations in Java are methods defined in the Graphics class. You don't have to create an instance of Graphics in order to draw something in your applet; in your applet's paint() method (which you learned about yesterday), you are given a Graphics object. By drawing on that object, you draw onto your applet and the results appear onscreen.

The Graphics class is part of the java.awt package, so if your applet does any painting (as it usually will), make sure you import that class at the beginning of your Java file:

import java.awt.Graphics;

public class MyClass extends java.applet.Applet {
...
}

The Graphics Coordinate System

To draw an object on the screen, you call one of the drawing methods available in the Graphics class. All the drawing methods have arguments representing endpoints, corners, or starting locations of the object as values in the applet's coordinate system-for example, a line starts at the point 10,10 and ends at the point 20,20.

Java's coordinate system has the origin (0,0) in the top-left corner. Positive x values are to the right and positive y values are down. All pixel values are integers; there are no partial or fractional pixels. Figure 9.1 shows how you might draw a simple square by using this coordinate system.

Figure 9.1 : The Java graphics coordinate system.

Java's coordinate system is different from that of many painting and layout programs, which have their x and y in the bottom left. If you're not used to working with this upside-down graphics system, it may take some practice to get familiar with it.

Drawing and Filling

The Graphics class provides a set of simple built-in graphics primitives for drawing, including lines, rectangles, polygons, ovals, and arcs

.

Note
Bitmap images, such as GIF files, can also be drawn by using the Graphics class. You'll learn about this tomorrow.

Lines

To draw straight lines, use the drawLine() method. drawLine() takes four arguments: the x and y coordinates of the starting point and the x and y coordinates of the ending point. So, for example, the following MyLine class draws a line from the point 25,25 to the point 75,75. Note that the drawLine() method is defined in the Graphics class (as are all the other graphics methods you'll learn about today). Here we're using that method for the current graphics context stored in the variable g:

import java.awt.Graphics;

public class MyLine extends java.applet.Applet {
    public void paint(Graphics g) {
        g.drawLine(25,25,75,75);
    }
}

Figure 9.2 shows how the simple MyLine class looks in a Java-enabled browser such as Netscape.

Figure 9.2 : Drawing lines.

Rectangles

The Java graphics primitives provide not just one, but three kinds of rectangles:

For each of these rectangles, you have two methods to choose from: one that draws the rectangle in outline form and one that draws the rectangle filled with color.

To draw a plain rectangle, use either the drawRect() or fillRect() methods. Both take four arguments: the x and y coordinates of the top-left corner of the rectangle, and the width and height of the rectangle to draw. For example, the following class (MyRect) draws two squares: The left one is an outline and the right one is filled (Figure 9.3 shows the result):

Figure 9.3 : Rectangles.

import java.awt.Graphics;

public class MyRect extends java.applet.Applet {
    public void paint(Graphics g) {
        g.drawRect(20,20,60,60);
        g.fillRect(120,20,60,60);
    }
}

Rounded rectangles are, as you might expect, rectangles with rounded corners. The drawRoundRect() and fillRoundRect() methods to draw rounded rectangles are similar to regular rectangles except that rounded rectangles have two extra arguments for the width and height of the angle of the corners. Those two arguments determine how far along the edges of the rectangle the arc for the corner will start; the first for the angle along the horizontal plane, the second for the vertical. Larger values for the angle width and height make the overall rectangle more rounded; values equal to the width and height of the rectangle itself produce a circle. Figure 9.4 shows some examples of rounded corners.

Figure 9.4 : Rounded corners.

The following is a paint() method inside a class called MyRRect that draws two rounded rectangles: one as an outline with a rounded corner 10 pixels square; the other, filled, with a rounded corner 20 pixels square (Figure 9.5 shows the resulting squares):

Figure 9.5 : Rounded rectangles.

import java.awt.Graphics;

public class MyRRect extends java.applet.Applet {
    public void paint(Graphics g) {
        g.drawRoundRect(20,20,60,60,10,10);
        g.fillRoundRect(120,20,60,60,20,20);
    }
}

Finally, there are three-dimensional rectangles. These rectangles aren't really 3D; instead, they have a slight shadow effect that makes them appear either raised or indented from the surface of the applet. Three-dimensional rectangles have four arguments for the x and y of the start position and the width and height of the rectangle. The fifth argument is a boolean indicating whether the 3D effect is to raise the rectangle (true) or indent it (false). As with the other rectangles, there are also different methods for drawing and filling: draw3DRect() and fill3DRect(). The following is a class called My3DRect, which produces two 3D squares-the left one raised, the right one indented (Figure 9.6 shows the result):

Figure 9.6 : Three dimensional rectangles.

import java.awt.Graphics;

public class My3DRect extends java.applet.Applet {
    public void paint(Graphics g) {
        g.draw3DRect(20,20,60,60,true);
        g.draw3DRect(120,20,60,60,false);
    }
}

Note
The 3D rectangles in Figure 9.6 don't look very 3D, do they? In the current version of the Java Developer's Kit, it is extremely difficult to see the 3D effect on 3D rectangles, due to a very small line width. If you are having troubles with 3D rectangles, this may be why. Drawing 3D rectangles in any color other than black makes them easier to see.

Polygons

Polygons are shapes with an unlimited number of sides. To draw a polygon, you need a set of x and y coordinates. The polygon is then drawn as a set of straight lines from the first point to the second, the second to the third, and so on.

As with rectangles, you can draw an outline or a filled polygon (using the drawPolygon() and fillPolygon() methods, respectively). You also have a choice of how you want to indicate the list of coordinates-either as arrays of x and y coordinates or as an instance of the Polygon class.

Using the first way of drawing polygons, the drawPolygon() and fillPolygon() methods take three arguments:

The x and y arrays should, of course, have the same number of elements.

Here's an example of drawing a polygon's outline using this method (Figure 9.7 shows the result):

Figure 9.7 : A polygon.

import java.awt.Graphics;

public class MyPoly extends java.applet.Applet {
    public void paint(Graphics g) {
        int exes[] = { 39,94,97,142,53,58,26 };
        int whys[] = { 33,74,36,70,108,80,106 };
        int pts = exes.length;

        g.drawPolygon(exes,whys,pts);
    }
}

Note that Java does not automatically close the polygon; if you want to complete the shape, you have to include the starting point of the polygon at the end of the array. Drawing a filled polygon, however, joins the starting and ending points.

The second way of calling drawPolygon() and fillPolygon() is to use a Polygon object to store the individual points of the polygon. The Polygon class is useful if you intend to add points to the polygon or if you're building the polygon on-the-fly. Using the Polygon class, you can treat the polygon as an object rather than having to deal with individual arrays.

To create a polygon object, you can either first create an empty polygon:

Polygon poly = new Polygon();

or create a polygon from a set of points using integer arrays, as in the previous example:

int exes[] = { 39,94,97,142,53,58,26 };
int whys[] = { 33,74,36,70,108,80,106 };
int pts = exes.length;
Polygon poly = new Polygon(exes,whys,pts);

Once you have a polygon object, you can add points to the polygon as you need to:

poly.addPoint(20,35);

Then, to draw the polygon, just use the polygon object as an argument to drawPolygon() or fillPolygon(). Here's that previous example, rewritten this time with a Polygon object. You'll also fill this polygon rather than just drawing its outline (Figure 9.8 shows the output):

Figure 9.8 : Another polygon.

import java.awt.Graphics;

public class MyPoly2 extends java.applet.Applet {
    public void paint(Graphics g) {
        int exes[] = { 39,94,97,142,53,58,26 };
        int whys[] = { 33,74,36,70,108,80,106 };
        int pts = exes.length;
        Polygon poly = new Polygon(exes,whys,pts);
        g.fillPolygon(poly);
    }
}

Ovals

You use ovals to draw ellipses or circles. Ovals are just like rectangles with overly rounded corners. You draw them using four arguments: the x and y of the top corner, and the width and height of the oval itself. Note that because you're drawing an oval, the starting point is some distance to the left and up from the actual outline of the oval itself. Again, if you think of it as a rectangle, it's easier to place.

As with the other drawing operations, the drawOval() method draws an outline of an oval, and the fillOval() method draws a filled oval.

The following example draws two ovals-a circle and an ellipse (Figure 9.9 shows how these two ovals appear onscreen):

Figure 9.9 : Ovals.

import java.awt.Graphics;

public class MyOval extends java.applet.Applet {
    public void paint(Graphics g) {
        g.drawOval(20,20,60,60);
        g.fillOval(120,20,100,60);
    }
}

Arcs

Of all the shapes you can construct using methods in the Graphics class, arcs are the most complex to construct, which is why I saved them for last. An arc is a part of an oval; in fact, the easiest way to think of an arc is as a section of a complete oval. Figure 9.10 shows some arcs.

Figure 9.10: Arcs.

The drawArc() method takes six arguments: the starting corner, the width and height, the angle at which to start the arc, and the degrees to draw it before stopping. Once again, there is a drawArc method to draw the arc's outline and the fillArc() method to fill the arc. Filled arcs are drawn as if they were sections of a pie; instead of joining the two endpoints, both endpoints are joined to the center of the circle.

The important thing to understand about arcs is that you're actually formulating the arc as an oval and then drawing only some of that. The starting corner and width and height are not the starting point and width and height of the actual arc as drawn on the screen; they're the width and height of the full ellipse of which the arc is a part. Those first points determine the size and shape of the arc; the last two arguments (for the degrees) determine the starting and ending points.

Let's start with a simple arc, a C shape on a circle, as shown in Figure 9.11.

Figure 9.11: A C arc.

To construct the method to draw this arc, the first thing you do is think of it as a complete circle. Then you find the x and y coordinates and the width and height of that circle. Those four values are the first four arguments to the drawArc() or fillArc() methods. Figure 9.12 shows how to get those values from the arc.

Figure 9.12: Constructing a circular arc.

To get the last two arguments, think in degrees around the circle, going counterclockwise. Zero degrees is at 3 o'clock, 90 degrees is at 12 o'clock, 180 at 9 o'clock, and 270 at 6 o'clock. The start of the arc is the degree value of the start of the arc. In this example, the starting point is the top of the C at 90 degrees; 90 is the fifth argument.

The sixth and last argument is another degree value indicating how far around the circle to sweep and the direction to go in (it's not the ending degree angle, as you might think). In this case, because you're going halfway around the circle, you're sweeping 180 degrees-and 180 is therefore the last argument in the arc. The important part is that you're sweeping 180 degrees counterclockwise, which is in the positive direction in Java. If you are drawing a backwards C, you sweep 180 degrees in the negative direction, and the last argument is -180. See Figure 9.13 for the final illustration of how this works.

Figure 9.13: Arcs on circles.

Note
It doesn't matter which side of the arc you start with. Because the shape of the arc has already been determined by the complete oval it's a section of, starting at either endpoint will work.

Here's the code for this example; you'll draw an outline of the C and a filled C to its right, as shown in Figure 9.14:

Figure 9.14: Two circular arcs.

import java.awt.Graphics;

public class MyOval extends java.applet.Applet {
    public void paint(Graphics g) {
        g.drawArc(20,20,60,60,90,180);
        g.fillArc(120,20,60,60,90,180);
    }
}

Circles are an easy way to visualize arcs on circles; arcs on ellipses are slightly more difficult. Let's go through this same process to draw the arc shown in Figure 9.15.

Figure 9.15: An elliptical arc.

Like the arc on the circle, this arc is a piece of a complete oval, in this case, an elliptical oval. By completing the oval that this arc is a part of, you can get the starting points and the width and height arguments for the drawArc() or fillArc() method (see Figure 9.16).

Figure 9.16: Arcs on ellipses.

Then all you need is to figure out the starting angle and the angle to sweep. This arc doesn't start on a nice boundary such as 90 or 180 degrees, so you'll need some trial and error. This arc starts somewhere around 25 degrees, and then sweeps clockwise about 130 degrees (see Figure 9.17).

Figure 9.17: Starting and ending points.

With all portions of the arc in place, you can write the code. Here's the Java code for this arc, both drawn and filled (note in the filled case how filled arcs are drawn as if they were pie sections):

import java.awt.Graphics;

public class MyOval extends java.applet.Applet {
    public void paint(Graphics g) {
        g.drawArc(10,20,150,50,25,-130);
        g.fillArc(10,80,150,50,25,-130);
    }
}

Figure 9.18 shows the two elliptical arcs.

Figure 9.18: Two elliptical arcs.

To summarize, here are the steps to take to construct arcs in Java:

  1. Think of the arc as a slice of a complete oval.
  2. Construct the full oval with the starting point and the width and height (it often helps to draw the full oval on the screen to get an idea of the right positioning).
  3. Determine the starting angle for the beginning of the arc.
  4. Determine how far to sweep the arc and in which direction (counterclockwise indicates positive values, clockwise indicates negative).

A Simple Graphics Example

Here's an example of an applet that uses many of the built-in graphics primitives to draw a rudimentary shape. In this case, it's a lamp with a spotted shade (or a sort of cubist mushroom, depending on your point of view). Listing 9.1 has the complete code for the lamp; Figure 9.19 shows the resulting applet.

Figure 9.19: The Lamp applet.


Listing 9.1. The Lamp class.

 1: import java.awt.*;
 2: 
 3: public class Lamp extends java.applet.Applet {
 4: 
 5:    public void paint(Graphics g) {
 6:        // the lamp platform
 7:        g.fillRect(0,250,290,290);
 8:
 9:        // the base of the lamp
10:        g.drawLine(125,250,125,160);
11:        g.drawLine(175,250,175,160);
12: 
13:        // the lamp shade, top and bottom edges
14:         g.drawArc(85,157,130,50,-65,312);
15:         g.drawArc(85,87,130,50,62,58);
16: 
17:         // lamp shade, sides
18:         g.drawLine(85,177,119,89);
19:         g.drawLine(215,177,181,89);
20: 
21:         // dots on the shade
22:         g.fillArc(78,120,40,40,63,-174);
23:         g.fillOval(120,96,40,40);
24:         g.fillArc(173,100,40,40,110,180);
25:    }
26: }

Copying and Clearing

Once you've drawn a few things on the screen, you may want to move them around or clear the entire applet. The Graphics class provides methods for doing both these things.

The copyArea() method copies a rectangular area of the screen to another area of the screen. copyArea() takes six arguments: the x and y of the top corner of the rectangle to copy, the width and the height of that rectangle, and the distance in the x and y directions to which to copy it. For example, this line copies a square area 100 pixels on a side 100 pixels directly to its right:

g.copyArea(0,0,100,100,100,0);

To clear a rectangular area, use the clearRect() method. clearRect(), which takes the same four arguments as the drawRect() and fillRect() methods, fills the given rectangle with the current background color of the applet (you'll learn how to set the current background color later today).

To clear the entire applet, you can use the size() method, which returns a Dimension object representing the width and height of the applet. You can then get to the actual values for width and height by using the width and height instance variables:

g.clearRect(0,0,size().width,size().height);

Text and Fonts

Using the Graphics class, you can also print text on the screen, in conjunction with the Font class (and, sometimes, the FontMetrics class). The Font class represents a given font-its name, style, and point size-and FontMetrics gives you information about that font (for example, the actual height or width of a given character) so that you can precisely lay out text in your applet.

Note that the text here is drawn to the screen once and intended to stay there. You'll learn about entering text from the keyboard later this week.

Creating Font Objects

To draw text to the screen, first you need to create an instance of the Font class. Font objects represent an individual font-that is, its name, style (bold, italic), and point size. Font names are strings representing the family of the font, for example, "TimesRoman", "Courier", or "Helvetica". Font styles are constants defined by the Font class; you can get to them using class variables-for example, Font.PLAIN, Font.BOLD, or Font.ITALIC. Finally, the point size is the size of the font, as defined by the font itself; the point size may or may not be the height of the characters.

To create an individual font object, use these three arguments to the Font class's new constructor:

Font f = new Font("TimesRoman", Font.BOLD, 24);

This example creates a font object for the TimesRoman BOLD font, in 24 points. Note that like most Java classes, you have to import the java.awt.Font class before you can use it.

Tip
Font styles are actually integer constants that can be added to create combined styles; for example, Font.BOLD + Font.ITALIC produces a font that is both bold and italic.

The fonts you have available to you in your applets depend on which fonts are installed on the system where the applet is running. If you pick a font for your applet and that font isn't available on the current system, Java will substitute a default font (usually Courier). You can get an array of the names of the current fonts available in the system using this bit of code:

String[] fontslist = this.getToolkit().getFontList();

From this list, you can then often intelligently decide which fonts you want to use in your applet. For best results, however, it's a good idea to stick with standard fonts such as "TimesRoman", "Helvetica", and "Courier".

Drawing Characters and Strings

With a font object in hand, you can draw text on the screen using the methods drawChars() and drawString(). First, though, you need to set the current font to your font object using the setFont() method.

The current font is part of the graphics state that is kept track of by the Graphics object on which you're drawing. Each time you draw a character or a string to the screen, Java draws that text in the current font. To change the font of the text, therefore, first change the current font. The following paint() method creates a new font, sets the current font to that font, and draws the string "This is a big font.", at the point 10,100:

public void paint(Graphics g) {
    Font f = new Font("TimesRoman", Font.PLAIN, 72);
    g.setFont(f);
    g.drawString("This is a big font.", 10, 100);
}

This should all look familiar to you; this is how the Hello World and Hello Again applets throughout this book were produced.

The latter two arguments to drawString() determine the point where the string will start. The x value is the start of the leftmost edge of the text; y is the baseline for the entire string.

Similar to drawString() is the drawChars() method that, instead of taking a string as an argument, takes an array of characters. drawChars() has five arguments: the array of characters, an integer representing the first character in the array to draw, another integer for the last character in the array to draw (all characters between the first and last are drawn), and the x and y for the starting point. Most of the time, drawString() is more useful than drawChars().

Listing 9.2 shows an applet that draws several lines of text in different fonts; Figure 9.20 shows the result.

Figure 9.20: The output of the ManyFonts applet.


Listing 9.2. Many different fonts.
 1: import java.awt.Font;
 2: import java.awt.Graphics;
 3:
 4: public class ManyFonts extends java.applet.Applet {
 5:
 6:    public void paint(Graphics g) {
 7:        Font f = new Font("TimesRoman", Font.PLAIN, 18);
 8:        Font fb = new Font("TimesRoman", Font.BOLD, 18);
 9:        Font fi = new Font("TimesRoman", Font.ITALIC, 18);
10:        Font fbi = new Font("TimesRoman", Font.BOLD + Font.ITALIC, 18);
11:
12:        g.setFont(f);
13:        g.drawString("This is a plain font", 10, 25);
14:        g.setFont(fb);
15:        g.drawString("This is a bold font", 10, 50);
16:        g.setFont(fi);
17:        g.drawString("This is an italic font", 10, 75);
18:        g.setFont(fbi);
19:        g.drawString("This is a bold italic font", 10, 100);
20:    }
21:
22: }

Finding Out Information About a Font

Sometimes you may want to make decisions in your Java program based on the qualities of the current font-for example, its point size and the total height of its characters. You can find out some basic information about fonts and font objects by using simple methods on Graphics and on the Font objects. Table 9.1 shows some of these methods.

Table 9.1. Font methods.

Method NameIn Object Action
getFont() Graphics Returns the current font object as previously set by setFont()
getName() Font Returns the name of the font as a string
getSize() Font Returns the current font size (an integer)
getStyle() Font Returns the current style of the font (styles are integer constants: 0 is plain, 1 is bold, 2 is italic, 3 is bold italic)
isPlain() Font Returns true or false if the font's style is plain
isBold() Font Returns true or false if the font's style is bold
isItalic() Font Returns true or false if the font's style is italic

For more detailed information about the qualities of the current font (for example, the length or height of given characters), you need to work with font metrics. The FontMetrics class describes information specific to a given font: the leading between lines, the height and width of each character, and so on. To work with these sorts of values, you create a FontMetrics object based on the current font by using the applet method getFontMetrics():

Font f = new Font("TimesRoman", Font.BOLD, 36);
FontMetrics fmetrics = getFontMetrics(f);
g.setfont(f);

Table 9.2 shows some of the things you can find out using font metrics. All these methods should be called on a FontMetrics object.

Table 9.2. Font metrics methods.

Method NameAction
stringWidth(string) Given a string, returns the full width of that string, in pixels
charWidth(char) Given a character, returns the width of that character
getAscent() Returns the ascent of the font, that is, the distance between the font's baseline and the top of the characters
getDescent() Returns the descent of the font-that is, the distance between the font's baseline and the bottoms of the characters (for characters such as p and q that drop below the baseline)
getLeading() Returns the leading for the font, that is, the spacing between the descent of one line and the ascent of another line
getHeight() Returns the total height of the font, which is the sum of the ascent, descent, and leading value

As an example of the sorts of information you can use with font metrics, Listing 9.3 shows the Java code for an applet that automatically centers a string horizontally and vertically inside an applet. The centering position is different depending on the font and font size; by using font metrics to find out the actual size of a string, you can draw the string in the appropriate place.

Figure 9.21 shows the result (which is less interesting than if you actually compile and experiment with various applet and font sizes).

Figure 9.21: The centered text.


Listing 9.3. Centering a string.
 1: import java.awt.Font;
 2: import java.awt.Graphics;
 3: import java.awt.FontMetrics;
 4:
 5: public class Centered extends java.applet.Applet {
 6:
 7:    public void paint(Graphics g) {
 8:        Font f = new Font("TimesRoman", Font.PLAIN, 36);
 9:        FontMetrics fm = getFontMetrics(f);
10:        g.setFont(f);
11:
12:        String s = "This is how the world ends.";
13:        int xstart = (size().width - fm.stringWidth(s)) / 2;
14:        int ystart = size().height / 2;
15:
16:        g.drawString(s, xstart, ystart);
17:    }
18:}

Analysis
Note the size() method in lines 13 and 14, which returns the width and height of the overall applet area as a Dimension object. You can then get to the individual width and height using the width and height instance variables of that Dimension, here by chaining the method call and the variable name. Getting the current applet size in this way is a better idea than hard coding the size of the applet into your code; this code works equally well with an applet of any size.

Note also that the line of text, as shown in Figure 9.21, isn't precisely vertically centered in the applet bounding box. This example centers the baseline of the text inside the applet; using the getAscent() and getDescent() methods from the FontMetrics class (to get the number of pixels from the baseline to the top of the characters and the number of pixels from the baseline to the bottom of the characters), you can figure out exactly the middle of the line of text.

Color

Drawing black lines and text on a gray background is all very nice, but being able to use different colors is much nicer. Java provides methods and behaviors for dealing with color in general through the Color class, and also provides methods for setting the current foreground and background colors so that you can draw with the colors you created.

Java's abstract color model uses 24-bit color, wherein a color is represented as a combination of red, green, and blue values. Each component of the color can have a number between 0 and 255. 0,0,0 is black, 255,255,255 is white, and Java can represent millions of colors between as well.

Java's abstract color model maps onto the color model of the platform Java is running on, which usually has only 256 or fewer colors from which to choose. If a requested color in a color object is not available for display, the resulting color may be mapped to another or dithered, depending on how the browser viewing the color implemented it, and depending on the platform on which you're running. In other words, although Java gives the capability of managing millions of colors, very few may actually be available to you in real life.

Using Color Objects

To draw an object in a particular color, you must create an instance of the Color class to represent that color. The Color class defines a set of standard color objects, stored in class variables, to quickly get a color object for some of the more popular colors. For example, Color.red returns a Color object representing red (RGB values of 255, 0, and 0), Color.white returns a white color (RGB values of 255, 255, and 255), and so on. Table 9.3 shows the standard colors defined by variables in the Color class.

Table 9.3. Standard colors.

Color NameRGB Value
Color.white 255,255,255
Color.black 0,0,0
Color.lightGray 192,192,192
Color.gray 128,128,128
Color.darkGray 64,64,64
Color.red 255,0,0
Color.green 0,255,0
Color.blue 0,0,255
Color.yellow 255,255,0
Color.magenta 255,0,255
Color.cyan 0,255,255
Color.pink 255,175,175
Color.orange 255,200,0

If the color you want to draw in is not one of the standard Color objects, fear not. You can create a color object for any combination of red, green, and blue, as long as you have the values of the color you want. Just create a new color object:

Color c = new Color(140,140,140);

This line of Java code creates a color object representing a dark gray. You can use any combination of red, green, and blue values to construct a color object.

Alternatively, you can create a color object using three floats from 0.0 to 1.0:

Color c = new Color(0.55,0.55,0.55);

Testing and Setting the Current Colors

To draw an object or text using a color object, you have to set the current color to be that color object, just as you have to set the current font to the font in which you want to draw. Use the setColor() method (a method for Graphics objects) to do this:

g.setColor(Color.green);

After you set the current color, all drawing operations will occur in that color.

In addition to setting the current color for the graphics context, you can also set the background and foreground colors for the applet itself by using the setBackground() and setForeground() methods. Both of these methods are defined in the java.awt.Component class, which Applet-and therefore your classes-automatically inherits.

The setBackground() method sets the background color of the applet, which is usually a light gray (to match the default background of the browser). It takes a single argument, a Color object:

setBackground(Color.white);

The setForeground() method also takes a single color as an argument, and it affects everything that has been drawn on the applet, regardless of the color in which it has been drawn. You can use setForeground() to change the color of everything in the applet at once, rather than having to redraw everything:

setForeground(Color.black);

In addition to the setColor(), setForeground(), and setBackground() methods, there are corresponding get methods that enable you to retrieve the current graphics color, background, or foreground. Those methods are getColor() (defined in Graphics objects), getForeground() (defined in Applet), and getBackground() (also in Applet). You can use these methods to choose colors based on existing colors in the applet:

setForeground(g.getColor());

A Simple Color Example

Listing 9.4 shows the code for an applet that fills the applet's drawing area with square boxes, each of which has a randomly chosen color in it. It's written so that it can handle any size of applet and automatically fill the area with the right number of boxes.


Listing 9.4. Random color boxes.
 1:  import java.awt.Graphics;
 2:  import java.awt.Color;
 3:
 4:  public class ColorBoxes extends java.applet.Applet {
 5:
 6:      public void paint(Graphics g) {
 7:          int rval, gval, bval;
 8:
 9:          for (int j = 30; j < (size().height -25); j += 30)
10:             for (int i = 5; i < (size().width -25); i += 30) {
11:                 rval = (int)Math.floor(Math.random() * 256);
12:                 gval = (int)Math.floor(Math.random() * 256);
13:                 bval = (int)Math.floor(Math.random() * 256);
14:
15:                 g.setColor(new Color(rval,gval,bval));
16:                 g.fillRect(i, j, 25, 25);
17:                 g.setColor(Color.black);
18:                 g.drawRect(i-1, j-1, 25, 25);
19:             }
20:     }
21: }

Analysis
The two for loops are the heart of this example; the first one draws the rows, and the second draws the individual boxes within each row. When a box is drawn, the random color is calculated first, and then the box is drawn. A black outline is drawn around each box, because some of them tend to blend into the background of the applet.

Because this paint method generates new colors each time the applet is painted, you can regenerate the colors by moving the window around or by covering the applet's window with another one (or by reloading the page). Figure 9.22 shows the final applet (although given that this picture is black and white, you can't get the full effect of the multicolored squares).

Figure 9.22: The random colors applet.

Summary

You present something on the screen by painting inside your applet: shapes, graphics, text, or images. Today you have learned the basics of how to paint, including how to use the graphics primitives to draw rudimentary shapes, how to use fonts and font metrics to draw text, and how to use Color objects to change the color of what you're drawing on the screen. It's this foundation in painting that enables you to do animation inside an applet (which basically involves just painting repeatedly to the screen) and to work with images. These are topics you'll learn about tomorrow.

Q&A

Q:
In all the examples you show, and in all the tests I've made, the graphics primitives, such as drawLine() and drawRect(), produce lines that are one pixel wide. How can I draw thicker lines?
A:
In the current state of the Java Graphics class, you can't; no methods exist for changing the default line width. If you really need a thicker line, you have to draw multiple lines one pixel apart to produce that effect.
Q:
I want to draw a line of text with a boldface word in the middle. I understand that I need two font objects-one for the regular font and one for the bold one-and that I'll need to reset the current font in between. The problem is that drawString() requires an x and a y position for the start of each string, and can't find anything that refers to "current point." How can I figure out where to start the boldface word?
A:
Java's text display capabilities are fairly primitive. There is no concept of the current point, so you'll have to manually figure out where the end of one string was so that you can begin the next string. The stringWidth() methods can help you with that, both to find out the width of the string you just drew and to add the space after it.
Q:
How do I use non-roman fonts such as kanji in Java?
A:
Java's support for international fonts in the 1.0.2 version of the JDK is sketchy, beyond the encoding of the raw characters as Unicode. Your best bet is to wait for the 1.1 version of the JDK, which will offer much more flexibility in the way of Unicode character display, support for internationalization, and non-roman fonts.
Q:
I tried out the applet that draws boxes with random colors, but each time it draws, a lot of the boxes are the same color. If the colors are truly random, why is it doing this?
A:
Two reasons. The first is that the random number generator I used in that code (from the Math class) isn't a very good random number generator; in fact, the documentation for that method says as much. For a better random number generator, use the Random class from the java.util package.
 
The second, more likely, reason is that there just aren't enough colors available in your browser or on your system to draw all the colors that the applet is generating. If your system can't produce the wide range of colors available using the Color class, or if the browser has allocated too many colors for other things, you may end up with duplicate colors in the boxes, depending on how the browser and the system have been written to handle that. Usually your applet won't use quite so many colors, so you won't run into this problem quite so often.
Q:
I have a tiled background on my Web page. I can create images with transparent backgrounds so that the tiled page background shows through. Can I create transparent applets?
A:
Not with the 1.02 JDK (and perhaps not with 1.1 either). For applets, your best bet is to use a plain-colored background and set your applet's background to be that same color.

Another idea if you use a tile for the page background is to import that image and draw it as the background for your applet (you'll learn about images tomorrow). However, using that mechanism, it is unlikely that the edges of the tile will exactly match up. Unfortunately, there doesn't appear to be a good workaround for this problem.