This chapter covers the various tools Java provides to help you build the user interface for your Java application or applet. Java provides a rich set of tools to make platform-independent, easy-to-use graphical user interfaces. Your Java project can use three groups of interface elements:
If you've ever programmed a graphical user interface (GUI) for the Mac, Windows, or UNIX, you'll find that the basic tools are familiar. Java is catching on because, unlike previous languages, the interface is pretty much platform independent so that you don't have to maintain multiple code bases-one for each target platform.
Although Java is technically platform independent, discrepancies still exist between platforms. These differences result from three different mechanisms:
Because you can't really do much about any of these issues, you
need to test your projects on as many different platforms as you
think they will run on. For applets that are used on the Internet,
that means pretty much every platform, whereas for an application
running on your company's intranet, it might mean only Sun and
SGI. The good news is that for simple interfaces, you'll find
that although everything doesn't look identical across platforms,
the matchup is pretty good. If you don't have access to multiple
platforms, you can be fairly sure that your interface will look
pretty much the same on any platform that supports Java.
| Note |
If the content of the WWW site on which you're going to use the applet is machine specific, you probably don't have to worry about the applet not being pretty on other platforms. If you have a Mac software site, you probably won't get a whole lot of visits from Windows users. |
The Abstract Window Toolkit (or Alternative Window Toolkit, depending on who you talk to) is a visual interface kit that comes with all the current implementations of Java. As its name indicates, though, the intent is for other-hopefully, more powerful and easier to use-toolkits to be developed by various vendors in the future. For now, if you want to do any interface work in Java, the AWT package is the way to go.
Figures 17.1 through 17.3 show the inheritance hierarchy of the classes commonly used in building interfaces.
Figure 17.1 : AWT classes that inherit from Java.lang object.
Figure 17.3 : The Applet.class inherits from java.awt.Panel,so you can draw directly to an applet.
These class structures might seem a bit intimidating at first, but if you glance through them, you'll see that most AWT classes just inherit from Object. In addition, all the interactive elements (except menus) inherit from Component. The only other very important thing to note is that because Applet inherits from Panel (which inherits from Container), applets can directly contain other objects such as buttons, canvases, and so on. This section describes how you can build hierarchies of containers in applets.
All the other classes are described as they become relevant in the following sections. Use the diagrams included in these sections as quick references when you want to find out what capabilities a class inherits.
The Graphics class is part of the AWT. It's contained in java.awt.Graphics, and it's the basic class for everything you'll draw on-screen. Applets have associated Graphics instances, as do various components such as buttons. Drawing methods, such as drawLine, work on a Graphics instance, so you'll see many calls in this form in a typical Java applet:
public void paint(Graphics g) {
g.drawLine(10,10,20,20);
}
The Graphics class uses a standard computer coordinate system with the origin in the upper- left corner, as shown in Figure 17.4.
Figure 17.4 : The Java Graphics class coordinate system.
All coordinate measurements in Java are done in pixels. The size of items, therefore, in absolute units such as inches or millimeters, differs on various machines due to differing pixels/inch values.
You'll find that whenever your program has to draw something, you'll be using Graphics class methods. The following sections discuss the most useful methods. Here's a listing of the methods that aren't covered later in this chapter, but that occasionally will be useful.
Changes the clipping region to the intersection of the current clipping rectangle and the one specified in the input arguments. This means that the clipping region can only get smaller.
Copies the rectangular area defined by x, y, width, and height to a rectangle defined by new_x, new_y, width, and height in the same Graphics object.
Makes a clone of the Graphics object.
Makes a clone of the Graphics object, but with a clipping region defined by the intersection of the current clipping rectangle and the one specified by x, y, width, and height. In addition, the origin of the Graphics object returned by this call is set to x,y.
Sets the painting mode to draw over; it is used to undo the effects of setXORMode.
Sets the drawing mode to exclusive OR. The color is bitwise exclusive ORed with the current color and the foreground color to produce the final color.
Translates the origin of the Graphics area to the specified point.
You'll encounter three key methods over and over again as you work with the various user interface elements.
Requests a redraw of an item or an entire interface. It then calls update.
Controls what happens when repaint is called; you can override this method.
Determines what's done when any item is redrawn. It's called whenever something needs to be redrawn-for example, when a window is uncovered. All displayable entities have paint methods that they inherit from Component.
| Note |
Interactive elements, such as buttons, are redrawn automatically; you don't have to implement a paint method for them. |
All these methods are defined in the Component class. This means that all the various interactive controls and applets inherit these methods. Although all these methods can be overridden, you'll find that paint and update are the ones you work with most often. Listing 17.1 shows a simple example of how paint and repaint can be used.
Listing 17.1. An example of paint and repaint.
import java.awt.*;
import java.applet.Applet;
public class paint_methods extends Applet{
int y;
public void init() {
y = 1;
}
public void start () {
while(true) {
y += 1;
repaint();
//wait 50 milliseconds and then call repaint again
try {
Thread.sleep(50);
} catch(InterruptedException e) {}
}
}
public void paint(Graphics g)
{
//draw a string to the screen
g.drawString("Hello, World!", 25, y );
}
}
After you run this code, you see the phrase Hello, World! slide down the screen. You're probably wondering why you don't see many copies of Hello, World! on-screen at the same time. The answer is the update method. Listing 17.2 shows the default update method.
Listing 17.2. The default update method.
public void update(Graphics g) {
g.setColor(getBackground()); //set the drawing color to the
//background color
g.fillRect(0,0,width,height); //fill the window with the
//background color
g.setColor(getForeground()); //reset the foreground color
paint(g); //call paint to redraw everything
}
The first three lines erase whatever is in the Graphics object so that anything that was drawn before it is deleted. You can change this behavior by overriding the update method. The version of the update method shown in Listing 17.3, when added to the applet in Listing 17.1, gives you the result shown in Figure 17.5 because the screen isn't erased between calls to paint.
Figure 17.5 : Overriding the update method.
Listing 17.3. A revised update method.
public void update(Graphics g) {
paint(g); //call paint to redraw everything
}
One thing to remember about calling the repaint method is that it doesn't always execute immediately. Java repaints as soon as it can, but sometimes repaints pile up because the computer is busy. Remember that your applet might be running on some fairly slow platforms; in this case, Java throws out some of the repaints, discarding the oldest repaints first. Because of this practice, three other versions of repaint are available with different input parameters.
Tries to call update for the number of milliseconds specified in the input argument. If update can't be called by the end of the specified time, repaint gives up. Note that no error is thrown if the method gives up. You will want to use this version primarily for animations, when you know that repaints will occur frequently.
Repaints only the rectangular part of the screen defined by x, y, width, and height.
Combines the previous two repaint versions so that only a portion of the screen is updated. If it can't be done before the specified time elapses (in milliseconds), it is skipped.
When you invoke repaint on a container, such as an applet, the AWT takes care of invoking update on all the items in the container-even ones inside other containers that are in the top-level container.
The AWT provides very generalized color support. The abstract ColorModel class enables you to define how colors are represented. The AWT comes with two predefined color models: IndexColorModel and DirectColorModel. IndexColorModel is used when images have a lookup table of colors and each pixel's value is an index in the lookup table. The pixel's color is determined by the color value in the lookup table specified by the index. DirectColorModel uses a 32-bit integer to hold the data for the color. Regardless of which color model you use, you still can represent individual colors as 32-bit integers (as described later in this section). In general, you won't have to worry about the ColorModel class.
The AWT's normal color model is RGB; in this model, a color is defined by its red, green, and blue components. Each of these three values is an int with a value between 0 and 255. An RGB value can be stored in an int with bits 31 through 24 being unused, bits 23 through 16 for the red value, bits 15 through 8 storing the green value, and bits 0 through 7 for the blue component of the color. The applet in Listing 17.4 shows how to extract the color components from an int.
Listing 17.4. Extracting color components from an integer.
import java.awt.*;
import java.applet.Applet;
public class unpack_color extends Applet
{
public void init()
{
//getRGB is a static method so you invoke it with the name of the
//class. You don't need a specific instance.
int temp = Color.white.getRGB();
unpack_colors(temp);
}
public void unpack_colors(int the_color)
{
int red, green, blue;
//the highest order byte is unused
//the next highest byte is red
red = the_color & 0x00FF0000;
//shift the value to the right so it's in the range 0-255
red = red >> 16;
//the next-to-last byte is green
green = the_color & 0x0000FF00;
//shift the value to the right so it's in the range 0-255
green = green >> 8;
//the lowest byte is blue
blue = the_color & 0x000000FF;
System.out.println("red = " + red + " green= " + green +
" blue= " + blue);
}
}
A Color class is available that enables you to use a single object rather than three ints to define a color. Most AWT methods that use colors require you to pass them an instance of a Color object.
The Color class also has
several methods for manipulating and creating colors.
| Note |
All of the method descriptions in this chapter start with the type of value returned by the method (if no type is shown it's void) followed by the method name and its input arguments. For example, |
Returns a new color that is approximately 1.5 times brighter than the current color.
Returns a color about 70 percent as bright as the original color.
Checks to see whether two colors are the same.
Returns the blue component of a color.
Returns an instance of the Color class given a string containing a decimal or hexadecimal number. For example, 0x0000FF produces a deep blue. This is a static method. This version and the next two versions are useful primarily when reading in parameters from HTML.
Performs the same function as the preceding, except if the string isn't in the right format, it returns the color defined by the second parameter.
Performs the same function as the preceding, except you supply a default packed integer with RGB values, not an instance of color.
Returns an instance of the Color class based on the supplied HSB values (hue, saturation, and brightness). This is a static method.
Returns the green component of the color.
Produces an integer containing the RGB values of a color-with blue in the lowest byte, green in the second byte, and red in the third byte-when given the three HSB values. This is a static method.
Returns the packed-integer version of the RGB values of a color.
Returns the red component of a color.
Returns an array of floats containing the h, s, and b values of the equivalent color to the specified r, g, and b values.
As you probably guessed from the names of some of these methods, the AWT also lets you work with the HSB model. In this model, colors are represented as hue, saturation, and brightness.
There are a number of reasons to use the HSB representation. You can convert a color image to black and white by just setting the saturation of all the pixels to 0. Similarly, if you want to create a continuous color spectrum, it's easy to smoothly change the hue to get a continuum-within the limits of the color support of your monitor and Web browser-of colors. The applet shown in Listing 17.5 creates the rainbow shown in Figure 17.6; trust me-even though you can see it only in black and white, it looks great in color.
Listing 17.5. Creating a rainbow.
import java.awt.*;
import java.applet.Applet;
public class rainbow extends Applet{
int n_steps;
int rainbow_width;
public void init()
{
}
public void paint(Graphics g)
{
float h,s,b;
int x,y,i;
Color temp_color;
int patch_width,patch_height;
float incr;
n_steps = 256;
//define how much the Hue will change between steps
incr = (float)(0.25*Math.PI/n_steps);
rainbow_width = 256;
//figure out how wide each step would be
patch_width = (int)(rainbow_width/n_steps);
patch_height = 50;
//fix the value of Saturation to the maximum value
s = (float)1.0;
//fix the value of Brightness to the maximum value
b = (float)1.0;
y = 40;
x = 0;
//draw a set of rectangles filled with the colors of
//the rainbow
for(i=0;i<n_steps;i++) {
h = incr*i;
//create a new color using the HSB parameters
temp_color = Color.getHSBColor(h,s,b);
//set the current drawing color to the new color
g.setColor(temp_color);
x += patch_width;
//draw a rectangle whose upper lefthand corner is at
//x,y and which is patch_width wide and patch_height
//tall
g.fillRect(x,y,patch_width,patch_height);
}
}
}
Several generally useful classes exist that are used to contain information about locations and shapes. They're used as arguments for many of the drawing-related methods.
Stores a width and a height as attributes. You can access these attributes with this code:
Dimension d;
int w,h;
h = d.height;
w = d.width;
Represents an x and y coordinate. The points x and y are accessible. Point also supports two methods.
Sets the x and y values of the point to the input parameter values.
Adds the input parameter values to the current x and y values of the point-for example, x = x + delta_x.
Represents an arbitrarily ordered list of vertices. You can directly access the three main attributes.
Specifies the array of x values for the vertices.
Specifies the array of y values. The nth y value and the nth x value define a vertex location.
Polygon has several useful methods.
Specifies the number of vertices.
Adds a new vertex to the polygon.
Returns the smallest rectangle that contains all the vertices.
Returns TRUE if the point defined by x and y is inside the polygon.
This has four public instance variables: the x and y coordinates of its upper-left corner (unless the height or width is negative, in which case the origin is in one of the other corners), its width, and its height. Rectangle has a number of methods that make working with rectangular regions easy.
Adds the specified x,y point to a Rectangle by growing the rectangle to the smallest rectangle that contains the original rectangle and the point.
Performs the same function as the preceding method, except the input parameter is a point rather than two integers.
Grows the rectangle to the smallest one that contains the original rectangle and the rectangle supplied as the input parameter.
Grows the rectangle by the specified amount in height and width. Each side of the rectangle moves a distance delta_w so that the overall width of the rectangle increases by 2*delta_w. If the original rectangle width and height are 50,50 and the two input parameters to grow are 50,75, the new width and height would be 150,200.
Returns TRUE if the point x,y is inside the rectangle.
Returns a rectangle that contains the intersection of the two rectangles. If the rectangles don't overlap, the resulting rectangle has 0 height and width.
Returns TRUE if the rectangles overlap, including just a shared edge or vertex.
Returns TRUE if the height or width of a rectangle is 0.
Sets the origin of the rectangle to x,y.
Sets the origin of the rectangle to x,y, its width to width, and its height to height.
Sets the width of the rectangle to width and its height to height.
Moves the rectangle a distance d_x in x and d_y in y.
Returns a new rectangle that is the smallest one that contains both rectangles.
This abstract class serves as a bridge between the platform-specific and the platform-independent parts of Java. It's the interface used to create peers for components such as buttons, and its methods let Java programs find out about platform-specific features such as the available fonts and the characteristics of the desktop. Because this is an abstract class, you don't instantiate it, but it does have a number of useful methods, including the following.
The Toolkit is your primary interface to machine-dependent information and interfaces. It's usable in applications and applets. Methods that are implemented by the Applet class, such as getImage, are available in applications via the Toolkit. You can use the static method getDefaultToolkit() to get the Toolkit, as in this snippet:
try {
Toolkit current_toolkit = Toolkit.getDefaultToolkit();
} catch(AWTError e) {}
| Note |
The peer of a component is the platform-specific interface definition of the methods that the component has to support. Implementing a Java component on a new platform (Linux, for example) consists of writing native methods to fill in the peer interface definition. |
Returns the screen size in pixels.
Returns the screen resolution in pixels/inch.
Returns the ColorModel, which defines how color is represented on the platform.
Gets a list of the names of the fonts available on the platform.
Returns the FontMetrics, which provide measurements of a font's screen size, for the specified font on the desktop.
Gets an image from the file specified in the input argument.
Gets an image from a specified URL.
Forces the loading of an image, with a given width and height. You can use the image observer to monitor the progress of loading the image. If width and height aren't the current dimensions of the image, the image is scaled.
Returns an integer that can be tested to determine the status of the image. ImageObserver constants that you can AND with the returned value are WIDTH, HEIGHT, PROPERTIES, SOMEBITS, FRAMEBITS, and ALLBITS. If the returned value ANDed with ALLBITS returns TRUE, the image is fully loaded.
Takes an image source, such as a filter, and creates an image.
The methods and tools described in this section enable you to draw simple graphical items that are non-interactive-although you can use the Canvas component and/or the location of mouse clicks to make these items behave as though they were interactive. If you've ever written code for a modern GUI, or even Windows, you'll find most of the drawing utilities fairly familiar. All these operations are implemented as methods on the Graphics class. Because anything you can draw on has a Graphics class as an attribute, this doesn't limit the utility of these functions.
You've already seen an example of drawing text using the drawString method. The general syntax for that method follows:
Graphics g;
g.drawString(String string_to_draw, int x_position, int y_position)
You also can use the drawChars method if you have an array of type char rather than a string.
Back in the dark ages, before the Mac and the desktop publishing revolution, people were used to having only a few fonts available for use on the computer. Things have changed dramatically since then; now people commonly buy CD-ROMs with more than 1,000 fonts for $50. Unfortunately, Java reintroduces a new generation to the wonders of limited fonts. This probably will change rapidly as Java and the Internet standards mature, but for right now, typographic simplicity is the order of the day.
You can create instances of the Font class using this creator syntax:
Font a_font = new Font(String name_of_font, int font_style, int font_size);
The generally available fonts follow:
Courier
Dialog
Helvetica
Symbol
TimesRoman
Because the set of available fonts may change, you might want to use a variation of the applet shown in Listing 17.6 to check for what fonts are available; this code works in applications as well.
Listing 17.6. Checking for available fonts.
import java.awt.*;
import java.applet.Applet;
public class discover_fonts extends Applet
{
public void paint(Graphics g){
String FontList[];
int i;
Font a_font;
FontList = getToolkit().getFontList();
for(i=0;i<FontList.length;i++) {
a_font = new Font(FontList[i],Font.BOLD,12);
g.setFont(a_font);
g.drawString("This is the " + FontList[i] +
" Font",25, 15*(i + 1));
}
}
}
This applet just gets a list of strings containing the names of the available fonts. Figure 17.7 shows the output of this applet.
Figure 17.7 : The available fonts.
The last line is Symbol font. The style for all the fonts was set to bold by using the font constant Font.BOLD. You also can use Font.ITALIC and Font.PLAIN. You can combine styles by bitwise ORing them. This line produces a font that is both italic and bold, for example:
Font a_font = new Font("Helvetica",(Font.BOLD | Font.ITALIC),12);
| Note |
Not all fonts support all styles. Courier prints the same no matter what you set the style to, as you can see in Figure 17.7. |
You'll find that you often need to precisely position text in an applet. The FontMetrics class provides an easy way to find out how much space text drawn with a given instance of Font will be. Just in case you're not a typographic expert, Table 17.1 provides some quick definitions of the key font measurement terms, illustrated in Figure 17.8.
Figure 17.8 : Font terminology.
| Height | The height of the tallest character in a font. It's therefore the maximum vertical distance you need to reserve when drawing a string with the font. |
| Baseline | The bottom of all characters are positioned on this imaginary line. The descent part of a character, such as the bottom curl on a g, lies below this line. |
| Ascent | Measures the height of the character above the baseline. This can include the amount of whitespace recommended by the font designer. |
| Descent | Measures the height (or, more appropriately, depth) of the character below the baseline. This can include the amount of whitespace recommended by the font designer. |
The applet shown in Listing 17.7 shows you how to access the FontMetrics information. The result is shown in Figure 17.9.
Figure 17.9 : Viewing FontMetrics information.
Listing 17.7. Accessing FontMetrics information.
import java.awt.*;
import java.applet.Applet;
public class font_metrics extends Applet
{
public void paint(Graphics g){
Font a_font;
FontMetrics a_font_metric;
String the_message;
int string_width, font_height,font_ascent, font_descent;
int font_ascent_no_white, font_descent_no_white;
int y;
the_message = "Alien Space Monsters! ";
//Make a new font
a_font = new Font("Helvetica",Font.BOLD,16);
g.setFont(a_font);
//get the metrics for the font
a_font_metric = g.getFontMetrics();
//get the width of the message
string_width = a_font_metric.stringWidth(the_message);
//get the height of the font
font_height = a_font_metric.getHeight();
//get the ascent of the font; this includes whitespace
//recommended by the font designer
font_ascent = a_font_metric.getAscent();
//get the descent of the font; this includes whitespace
//recommended by the font designer
font_descent = a_font_metric.getDescent();
//get the ascent without whitespace
font_ascent_no_white = a_font_metric.getMaxAscent();
//get the descent without whitespace
font_descent_no_white = a_font_metric.getMaxDescent();
//now show these values to the user
y = 10;
g.drawString(the_message, 10,y);
y += font_height + 1;
g.drawString("Message width is " + string_width, 10,y);
y += font_height + 1;
g.drawString("Font height is " + font_height, 10,y);
y += font_height + 1;
g.drawString("Font ascent is " + font_ascent +
" without white space it's " + font_ascent_no_white , 10,y);
y += font_height + 1;
g.drawString("Font descent is " + font_descent +
" without white space it's " + font_descent_no_white , 10,y);
}
}
This information is useful in a number of ways. First, notice how the tops of the letters in the first line in Figure 17.9 are cut off. That's because when you specify the y coordinate for a string, you're specifying the location of the baseline-not the upper-left corner of the text that is being drawn. To figure out where to put the baseline, you just need to look at the value of the ascent. Instead of defining the first value of y as 10, just change it to this:
y = font_ascent + 2; //the extra 2 provides some whitespace
//that the font otherwise lacks
The first line now is completely visible, as Figure 17.10 shows.
Figure 17.10 : Keeping your text in sight.
Another common use for FontMetrics data is to center text in an area. The code in Listing 17.8 centers text in an area.
Listing 17.8. Centering text.
public void draw_centered_text(Graphics g, String the_msg,
int width, int height) {
int x,y;
FontMetrics fm;
fm = g.getFontMetrics();
//find out how much free space there is on either side of the string by
//subtracting the width of the string from the width of the window and
//dividing by 2
x = (width - fm.stringWidth(the_msg))/2;
//find out how much free space there is above
//and below the string's baseline
y = fm.getAscent() + (height - fm.getHeight())/2;
//draw the string so that the baseline is centered
g.drawString(the_msg,x,y);
}
| Note |
You can get the width and height of an applet's area by using the getSize method, which returns a Dimension value. The height() and width() methods of the Dimension class enable you to get the height and width values. |
Figure 17.11 shows the result of centering text in a window.
Figure 17.11 : Centering text.
Three methods for drawing lines currently are available in Java.
Listing 17.9 shows a small applet that demonstrates how to draw
straight lines, arcs, and a point.
| Note |
No special command for drawing individual pixels is available. Just use drawLine as shown in Listing 17.9; alternatively, you can use drawRect or fillRect, which are discussed later in this section. |
Listing 17.9. Drawing straight lines, arcs, and a point.
import java.awt.*;
import java.applet.Applet;
public class drawpoint extends Applet{
public void paint(Graphics g)
{
int x_final, y_final;
int i;
//this draws a line
g.drawLine(10,10,100,100);
//this draws a point at 10,30
g.drawLine(10,30,10,30);
//this draws an arc
g.drawArc(50,50,30,30,0,180);
//this draws a filled arc
g.fillArc(50,100,20,40,90,90);
}
}
This applet generates the picture shown in Figure 17.12.
Figure 17.12 : A point, a line, an arc and a filled arc.
Here are the full descriptions for the three line-drawing methods.
This draws a line between two points.
x_start: Starting x position
y_start: Starting y position
x_end: Final x position
y_end: Final y position
This routine draws an arc, a segment of an ellipse, or a circle, as Figure 17.13 shows.
Figure 17.13 : Parameters for drawArc and fillArc method.
x: Upper-left corner of the rectangle that contains the ellipse the arc is from.
y: Upper-left corner of the rectangle that contains the ellipse the arc is from.
width: The width of the rectangle that contains the ellipse the arc is from.
height: The height of the rectangle that contains the ellipse the arc is from.
start_angle: The angle at which the arc starts; 0 degrees is at the 3 o'clock position, and the value increases in the counterclockwise direction.
This is the same as drawArc, except that the arc is filled with the current color.
One of the key shortcomings with current Java drawing routines is that you can't specify a line width. All the drawing commands draw unit-width lines. If you want to draw thicker lines, you can follow an approach similar to the one shown in Listing 17.10.
Listing 17.10. Drawing thicker lines.
public void draw_thick_line(Graphics g, int x_start,
int y_start, int x_final,
int y_final, int width) {
int i;
for (i=0;i<width;i++) {
g.drawLine(x_start+i,y_start + i,x_final+i,y_final+i);
}
}
This method just draws several lines to make a thicker line. You can use the same approach for the shapes you'll see in the next section.
The final way to draw very complex lines is to use the drawPolygon method, which is described in the next section. With it, you can draw arbitrarily complex paths.
Java provides a standard set of methods for drawing typical geometric shapes, such as rectangles, ovals, and polygons. It also has a method for drawing 3D rectangles (rectangles with shading to make them look 3D), but because the line widths are so narrow, it's almost impossible to see the 3D effect under normal circumstances. The syntax for the various shape-drawing methods follow.
This draws a traditional rectangle.
x: Upper-left corner
y: Upper-left corner
width: Width of the rectangle
height: Height of the rectangle
This draws a rectangle with rounded corners. You define the amount of curvature by setting the width and height of the rectangle that contains the curved corner (see Figure 17.14).
Figure 17.14 : Defining the degree of roundness for rounded rectangles.
x: Upper-left corner
y: Upper-left corner
width: Width of the rectangle
height: Height of the rectangle
r_width: Width of the rectangle that defines the roundness of the corner
rectangle: Defines the roundness of the corner
r_height: Height of the rectangle that defines the roundness of the corner
This draws a rectangle with a 3D shadow. Because the lines are so thin, you can't really see the shadow.
x: Upper-left corner
y: Upper-left corner
width: Width of rectangle
height: Height of rectangle
flag: If TRUE, the rectangle is raised; if FALSE, the rectangle is indented
This takes two lists-one with x and one with y coordinates-and draws a line between successive points. The polygon will not close automatically. If you want a closed polygon, you must make sure that the first and last points have the same coordinates.
x: An array of integers with the x coordinates of the polygon's vertices
y: An array of integers with the y coordinates of the polygon's vertices
n_points: Number of vertices in the polygon
This draws an oval. If width and height have the same value, you get a circle.
x: Upper-left corner of the rectangle that contains the oval (see Figure 17.15)
y: Upper-left corner of the rectangle that contains the oval
width: Width of the rectangle that contains the oval
height: Height of the rectangle that contains the oval
Figure 17.15 : Defining the size of an oval with the width and height parameters.
For each method starting with "draw," there is another method that starts with "fill." The "fill" methods have the same parameters and behave in the same way, except that they draw versions of the shapes filled with the current color.
The applet in Listing 17.11 shows how the draw and fill versions of the various commands work. It produces the screen shown in Figure 17.16.
Figure 17.16 : The various drawing functions in action.
Listing 17.11. Using the draw and fill commands.
import java.awt.*;
import java.applet.Applet;
public class drawshapes extends Applet
{
public void paint(Graphics g)
{
int i;
int x,y,x_o,width,height;
//set the x coordinates for the polygon vertices
int x_list[] = {80,90,120,83,80};
//set the y coordinates for the two polygons
int y_list[] ={5,35,60,40,10};
int y_list_1[] = {70,95,120,100,70};
g.drawRect(10,5,20,20);
g.fillRect(10,27,20,20);
g.drawOval(50,5,20,20);
g.fillOval(50,27,20,20);
g.drawPolygon(x_list, y_list, 5);
g.fillPolygon(x_list,y_list_1,5);
g.drawRoundRect(130,5,20,20,5,5);
g.fillRoundRect(130,27,20,20,10,15);
g.draw3DRect(160,5,20,20,true);
g.draw3DRect(160,27,20,20,false);
width = 2;
height = 2;
x = 10;
x_o = 30;
y = 50;
//draw a set of ten regular and filled rectangles and ovals
for(i=0;i<10;i++) {
y += 25;
width += 1;
height += 2;
g.drawRect(x,y,width,height);
g.drawOval(x_o,y,width,height);
}
}
}
The 3D rectangles don't look all that three-dimensional. The effect is subtle, and you should experiment with it on different platforms and with different color combinations to find one that looks good. Figure 17.17 shows the result of an applet that draws two standard 3D rectangles-one raised and the other sunken-with white lines on a black background. It then draws two thick-sided 3D rectangles by drawing several 3D rectangles together. You can see-assuming that the print quality of the book is good enough-in the standard 3D rectangles that in both cases two sides of the rectangle are slightly darker than the other two sides. Just in case you had trouble seeing that (and I know that I did), this effect is fairly clear on the thick-sided 3D rectangles.
Figure 17.17 : The secret of 3D rectangles.
One of the keys to the success of Java is its capability to add animation and action to Web pages without requiring you to download huge video files. Even though Java is an interpreted language (assuming that your users' browsers don't have a JIT-Just In Time compiler-installed), it's more than fast enough for most animation tasks. Listing 17.12 shows a simple animation that moves a yellow square over a red and blue checkerboard pattern. Figure 17.18 shows a screen shot-unmoving, unfortunately.
Figure 17.18 : Animation in action-well use your imagination.
Listing 17.12. A simple animation applet.
import java.awt.*;
import java.applet.Applet;
public class simplest_animation extends Applet implements Runnable
{
int x_pos,y_pos;
int n_squares;
int dh,dw;
Color color_1,color_2;
Color square_color;
int square_size;
int square_step;
Thread the_thread;
public void start() {
if (the_thread == null) {
the_thread = new Thread(this);
the_thread.start();
}
}
public void stop() {
the_thread.stop();
}
public void run() {
while (true) {
repaint();
try {
the_thread.sleep(50);
} catch(InterruptedException e) {};
}
}
public void init()
{
int w_height, w_width;
//set the starting point of the moving square
x_pos = 0;
y_pos = 0;
//set the size of the moving square
square_size = 40;
//set how much the square moves between screen redraws
square_step = 2;
//specifies the number of squares on each side of the checkerboard
n_squares = 10;
//get the size of the applet's drawing area
w_height = size().height;
w_width = size().width;
//determine the size of the squares in the checkerboard
dh = w_height/n_squares;
dw = w_width/n_squares;
//set the colors for the checkerboard.
//Could have used Color.blue and
//Color.red instead of creating new color instances
color_1 = new Color(0,0,255);
color_2 = new Color(255,0,0);
//set the color for the moving square
square_color = new Color(255,255,0);
}
public void draw_check(Graphics g) {
int i,j,offset;
int x,y;
//this draws a checkerboard
offset = 0;
for(i=0;i<n_squares;i++) {
y = i * dh;
offset++;
for(j=0;j<n_squares;j++) {
x = j * dw;
if (((j + offset)% 2) > 0) {
g.setColor(color_1);
} else {
g.setColor(color_2);
}
g.fillRect(x,y,dw,dh);
}
}
}
public void paint(Graphics g)
{
//draw the blue and red checkerboard background
draw_check(g);
//increment the position of the moving square
x_pos += square_step;
y_pos += square_step;
//set the drawing color to the square color
g.setColor(square_color);
//draw a filled rectangle that moves
g.fillRect(x_pos,y_pos,square_size, square_size);
}
}
What you can't see unless you run the applet is that the whole image is flickering; you can see the background through the yellow square. All in all, the word that comes to mind is ugly-hardly the sort of thing that will help sell a Web page. You can reduce this flicker in several ways. A large part of the flicker occurs when repaint invokes the update method. You saw earlier in this chapter that the update method erases the applet by drawing a background colored rectangle. This erasing, followed by the drawing of the background rectangle, causes a lot of the flicker. You can avoid this by overriding the update method with this version:
public void update(Graphics g) {
paint(g);
}
This eliminates the flicker in most of the image, but you'll still see flicker in the region of the rectangle. That's because the background is drawn and then the square is drawn over it. Your eye sees the background and then the yellow square every time the screen is redrawn. One way to reduce the amount of drawing that has to be done is to tell the AWT to redraw only the part of the screen that has changed.
The version of the paint
method shown in Listing 17.13 does just that. It creates two rectangles:
one for where the square is and one for where it will be after
it's moved in this call to paint.
The union method on Rectangle
then is used to get the smallest rectangle that contains both
those rectangles. That rectangle is the one that contains all
the changes between the two frames. Next, clipRect
is used to tell the AWT to repaint only this rectangle.
| Note |
Instead of creating two new rectangles each time paint is called, you could save the second rectangle in an instance variable and then use it as the old rectangle in the next call to paint. |
Listing 17.13. Repainting the minimal area.
public void paint(Graphics g)
{
Rectangle old_r, new_r,to_repaint;
draw_check(getGraphics());
//Figure out where the square is now
old_r = new Rectangle(x_pos,y_pos,square_size,square_size);
//Figure out where the square will be after this method executes
new_r = new Rectangle((x_pos + square_step),(y_pos +
square_step),square_size, square_size);
//Find the smallest rectangle that contains the old and new positions
to_repaint = new_r.union(old_r);
//Tell Java to only repaint the areas that have been
// affected by the moving square
g.clipRect(to_repaint.x,to_repaint.y,
to_repaint.width,to_repaint.height);
x_pos += square_step;
y_pos += square_step;
g.setColor(square_color);
g.fillRect(x_pos,y_pos,square_size, square_size);
}
Although using clipRect reduces the amount of work the AWT has to do, it's still not fast enough to fool your eye. To do that, you need to use double buffering.
Double buffering involves doing all your drawing to an invisible, off-screen bitmap-an image, actually-and then copying that off-screen image to the applet. This is called bitblitting on the Mac and results in much faster drawing. Listing 17.14 shows the commented changes you need to make in the animation applet to do double buffering.
Listing 17.14. Using double buffering.
public class simple_animation extends Applet implements Runnable
{
....
//Define an image to use for offscreen drawing
Image offscreen;
Graphics offscreen_graphics;
....
public void init()
{
....
//create the offscreen image and get its Graphics instance
offscreen = createImage(size().width,size().height);
offscreen_graphics = offscreen.getGraphics();
//draw the background checkerboard
draw_check();
}
....
public void draw_check() {
int i,j,offset;
int x,y;
offset = 0;
for(i=0;i<n_squares;i++) {
y = i * dh;
offset++;
for(j=0;j<n_squares;j++) {
x = j * dw;
if (((j + offset)% 2) > 0) {
offscreen_graphics.setColor(color_1);
} else {
offscreen_graphics.setColor(color_2);
}
offscreen_graphics.fillRect(x,y,dw,dh);
}
}
}
public void paint(Graphics g)
{
Rectangle old_r, new_r,to_repaint;
old_r = new Rectangle(x_pos,y_pos,square_size,square_size);
new_r = new Rectangle((x_pos + square_step),(y_pos +
square_step),square_size, square_size);
to_repaint = new_r.union(old_r);
draw_check();
//just draw what's needed except for the first time
if (x_pos < 1) {
g.clipRect(to_repaint.x,to_repaint.y,
to_repaint.width,to_repaint.height);
}
x_pos += square_step;
y_pos += square_step;
//same as before but now the square is drawn to the offscreen image
offscreen_graphics.setColor(square_color);
offscreen_graphics.fillRect(x_pos,y_pos,square_size,
square_size);
//now that the offscreen image is all done draw the whole thing
//to the screen
g.drawImage(offscreen,0,0,this);
}
}
These items are the ones that enable the user to dynamically interact with your program. They range from buttons to text display areas. Although different operating systems tend to use slightly different interaction elements, the AWT provides a rich enough set that users on all platforms will feel pretty much at home. This is especially true because the visual representation of each item actually is generated by the host operating system on the machine on which the application or applet is running. In addition, freeware, shareware, and commercial widget kits already exist that extend the basic elements provided by the AWT (see the section "Extending the AWT," later in this chapter, for more details).
As you saw in Figure 17.3, all the active components (other than menus), such as Button, inherit from the Component class. The Component methods provide a wide selection of functionality applicable to any interactive graphical element. Although you can't create an instance of Component, you'll use its methods fairly often. Component has a lot of methods, but this section lists some of the ones you'll use fairly often. These methods are invoked in response to various types of events. In all cases, if the method returns TRUE, it means that the method has handled the event. If FALSE is returned, the event is passed up the event chain. You can use these methods on multiple objects to deal with the same event.
This method usually is overridden. It's called whenever an ACTION_EVENT occurs on a component. Events are discussed in Chapter 21, "Event Handling."
This is called when a KEY_PRESS or KEY_ACTION event reaches a component. The key parameter specifies which key was involved. You can use this to have components respond to key clicks.
This method is invoked when the component receives a KEY_RELEASE event.
This is called when the object receives a LOST_FOCUS event.
This is invoked when the component receives a MOUSE_DOWN event, caused by the user clicking the mouse inside the component. The x and y coordinates are in the coordinate system of the component, where 0,0 is in the upper-left corner.
This is invoked when the user drags the mouse with the mouse button down over the component, generating a MOUSE_DRAG event.
This is invoked each time the mouse goes over the component, generating a MOUSE_ENTER event.
This is called when the component receives a MOUSE_EXIT event. The x and y values-which are expressed in the component's coordinates-represent the first point outside the component's bounding rectangle that the mouse goes over.
Although Component has a large selection of methods, the following are the ones you'll use most often.
Returns the bounding rectangle that contains the component.
Monitors the status of an image as it's being composed. You can use this to wait to display a component, such as a Canvas, that uses an image until the image is ready.
Creates a new Image of the specified size.
Disables the component so that the user can't interact with it. (This is a synchronized method.) The AWT draws a disabled component differently than an enabled one.
Enables a disabled component. This is a synchronized method.
Returns the color of the background for the component.
Returns the current font for the component.
Gets the FontMetrics, which contains information about the size of text on the current platform, for the component.
Returns the foreground color-the one that will be used to draw lines, fill shapes, and so on.
Gets the Graphics object associated with the component. You can then use drawing methods, such as fillRect, that are associated with the Graphics object to draw on the component.
Makes the component invisible. This is a synchronized method.
Returns TRUE if x,y lies inside the component's bounding rectangle. x and y should be specified in the coordinate system of the container that holds the component. The container's coordinate system origin is in the upper-left corner of the container. This is a synchronized method.
Sets a flag indicating that the component has been changed in a way that requires the Layout Manager to be called to lay out the screen again. A button's name might be made longer, for example, so the button will need to be resized.
Returns TRUE if the component is enabled to respond to user actions.
Returns TRUE if the component is visible in its parent's window. It can be visible but not showing if its height or width is 0 or if its location is outside the parent's window; for example, it might have been scrolled off-screen.
Returns TRUE if the component currently is visible. You can make a component invisible by invoking its hide method.
Returns a point that contains the coordinates of the component's origin.
Moves the component to the specified position in the parent container's coordinate system.
Redraws the component when it needs to be redrawn. Unless you want some custom behavior, the default method ensures that the component is drawn properly.
Enables you to get an image ready for display prior to displaying it on the component. Another version enables you to specify a size for the image so that it can be scaled.
Repaints this component by a specified time or cancels the request.
Repaints the specified part of the component.
Tries to repaint the specified region. If it can't do so before the specified time, it quits.
Enables you to specify the position and size of the component. This is a synchronized method.
Scales the component to fit in the defined bounding rectangle maintaining the same origin. This is the same as the version below except you specify the width and height separately rather than with a Dimension object.
Scales the component to fit in the defined bounding rectangle maintaining the same origin.
Sets the background color for a component. This is a synchronized method.
Specifies the font that will be used for any text drawn in the component. This is a synchronized method.
Sets the color used for drawing lines and filling in shapes. This is a synchronized method.
Makes the component visible if it had been hidden.
Returns the height and width of the component.
Erases the contents of the component's graphic area every time it's called.
Causes the component to see whether it or any of the components it contains is invalid. If any are invalid, the Layout Manager is called to bring things up-to-date. See the section "Buttons," later in this chapter, for an example of how to use invalidate/validate.
Remember that all the interactive interface elements, including containers such as applets and windows, inherit from Component.
The AWT containers contain classes that can contain other elements. Windows, panels, dialog boxes, frames, and applets are all containers. Whenever you want to display a component such as a button or pop-up menu, you'll use a container to hold it. The base class for all containers is-surprise! surprise!-the Container class.
The Container class has a number of methods that make it easy to add and remove components as well as to control the relative positioning and layout of those components. Containers can contain other containers, for example, so a window can contain several panels.
Container is an abstract class, and the methods you'll use most often follow.
Adds a component to the container.
Adds a component at the specified z position. This is a synchronized method. Be warned that the order of clipping based on relative z position may vary between machines. This problem should be fixed eventually, though.
Returns the number of top-level components of a container. It doesn't count components inside components; for example, a panel with three buttons inside a window is counted only as one component for the window.
Returns a reference to the index component. The index value is determined when the component is added. This is a synchronized method. This throws ArrayIndexOutOfBoundsException.
Returns an array of references to all the components in the container. This is a synchronized method.
Returns the insets object for the container. Insets define the empty space the Layout Manager reserves around the edge of the container-the minimum distance from the edge of a component to the edge of the container.
Removes the component from the container. This is a synchronized method.
Sets the Layout Manager the container will use. If you supply NULL as the argument, no Layout Manager is used; you can use absolute positioning.
Applet inherits from this class, so this section examines Panel in detail so that you can understand how the various demonstration applets work. The other container classes are discussed later in this section.
Panel inherits from Container.
It doesn't create its own window because it's used to group components
inside other containers. Panels enable you to group items in a
display in a way that might not be allowed by the available Layout
Managers. If you have a number of entries in your interface, for
example, that have a label and a text field, you can define a
panel that contains a label and a text field and add the panel
so that the label and the text field always stay together on the
same line (which wouldn't be the case if you added the two items
separately). Without the panel, the Layout Manager could put the
label and the text field on different lines. Panels also are useful
in Layout Managers in which only one item is allowed in an area,
such as the BorderLayout
Manager. By using a panel, you can put several components in a
single BorderLayout area,
such as North.
| Note |
A Layout Manager autopositions and sizes the various interface components, taking into account the screen resolution and the window size. |
An inset object defines the amount of empty space around the edge of a panel. The creator method for insets follows:
Insets, new Insets(int top, int left, int bottom, int right)
This defines a new Insets instance, which defines the boundaries specified by the input arguments.
You can change the amount of empty space, which is set to 0 by default, by overriding the Insets method of the container. The applet in Listing 17.15 defines its own panel class that does that. It defines a plain panel and a custom version that overrides the Insets method. Each of the items has four buttons added. Both items have white backgrounds so that you can see the size of the item, not just where the buttons are.
Listing 17.15. Defining a panel class.
import java.awt.*;
import java.applet.Applet;
public class HelloWorld extends Applet
{
public void init()
{
Panel a;
my_panel b;
GridLayout gl;
Button buttons[];
int i;
//create a new GridLayout to force the 4 buttons to arrange themselves
// in a 2 by 2 grid
gl = new GridLayout(2,2);
//create two panels to contain the 8 buttons
a = new Panel();
b = new my_panel();
//tell the panels to use the GridLayout manager rather than the default
//FlowLayout manager
a.setLayout(gl);
b.setLayout(gl);
//Make the backgrounds of the panels white so you
//can see them in the picture
a.setBackground(Color.white);
b.setBackground(Color.white);
//add the panels to the applet
add(a);
add(b);
//make the buttons and add them to the panels
buttons = new Button[8];
for(i=0; i< 8;i++) {
buttons[i] = new Button("Button " + i);
if (i <4) {
a.add(buttons[i]);
} else {
b.add(buttons[i]);
}
}
}
}
class my_panel extends Panel {
//This class exists so we can override the insets method
public Insets insets() {
return new Insets(5,10,15,20);
}
}
The applet generates the applet interface shown in Figure 17.19; look closely to see the white background for the first panel. Because the insets for the top panel default to 0, the panel background is the same size as the space required by the buttons. The custom panel is larger than the buttons because of the inset's value; the space on each side is different because the four values assigned to the inset are all different.
Figure 17.19 : Panels with the default insets and with custom insets.
A frame is a full-fledged, top-level, resizable window with a menu bar. You can specify the title, an icon, and a cursor. See the "Frames" section for examples.
This class isn't used very often, but it's a top-level window without borders and a menu bar.
Labels are text items that don't really do much. By using a label instead of drawString, you can use the Layout Managers to control text placement in a platform- and monitor-independent manner. The code in Listing 17.16 shows how to use labels. About the only significant flexibility you have, other than the alignment of the text, is the capability to change the font used (see Figure 17.20).
Figure 17.20 : Aligning text and modifying the fonts for labels.
Listing 17.16. Using labels.
import java.awt.*;
import java.applet.Applet;
public class label_example extends Applet{
Label the_labels[];
public void init(){
int i;
//set up an array of labels
the_labels = new Label[3];
//Create a label with the default format
the_labels[0] = new Label("on the left");
//these two commands show how to set the color of a Label
//the text itself is drawn with the Foreground color
the_labels[0].setBackground(Color.red);
the_labels[0].setForeground(Color.white);
//Make a new Font and then assign it to the label
Font a_font = new Font("TimesRoman",Font.PLAIN,24);
the_labels[0].setFont(a_font);
//Create a centered label
the_labels[1] = new Label("middle", Label.CENTER);
//Make a new Font and then assign it to the label
a_font = new Font("TimesRoman",Font.BOLD,24);
the_labels[1].setFont(a_font);
//Create a label aligned to the right
the_labels[2] = new Label("on the right", Label.RIGHT);
//Make a new Font and then assign it to the label
a_font = new Font("Helvetica",Font.ITALIC,18);
the_labels[2].setFont(a_font);
//these two commands show how to set the color of a Label
//the text itself is drawn with the Foreground color
the_labels[2].setBackground(Color.white);
the_labels[2].setForeground(Color.blue);
//add the three labels to the applet
for(i=0;i<3;i++) {
add(the_labels[i]);
}
}
}
The label creators and the most useful methods for the Label class follow:
Produces a label with the specified string.
Produces a label with the string aligned according to the second value, which should be one of the three constants Label.CENTER, Label.LEFT, or Label.RIGHT.
Returns the label string.
Changes the label text.
Java buttons are just like the buttons in every other GUI. They are text surrounded by a shape, and they generate an ACTION_EVENT event-the argument is a button's label-after the user clicks them. Java uses the native operating system-Mac, Windows, UNIX, and so on-to actually draw the buttons, so the look and feel of the buttons will be what is expected by users on each platform. Listing 17.17 shows a simple example of using buttons; see Chapter 21 for more information on handling events.
Listing 17.17. Using buttons.
import java.awt.*;
import java.applet.Applet;
public class buttons extends Applet{
Button a_button;
public void init()
{
//make a new button called "Howdy"
a_button = new Button("Howdy!");
//add the button to the Applet, which extends Panel
add(a_button);
}
public boolean action (Event e, Object o) {
Dimension d_should;
if (e.target instanceof Button) {
//check the button label and toggle between the two values
if (((String)o).equals("Howdy!")) {
a_button.setLabel("Alien Space Monster!");
} else {
a_button.setLabel("Howdy!");
}
//mark the button as having changed
a_button.invalidate();
//tell the applet to validate the layout
validate();
}
return true;
}
}
This applet starts out with a single button and then toggles the name of the button between two values every time the button is clicked. Figure 17.21 shows the applet before the button is clicked, and Figure 17.22 shows it afterward.
Figure 17.21 : A simple button.
Figure 17.22 : A button in action
Notice how the invalidate method, inherited from Component, is used to mark the button as having changed in a way that would affect the layout. That isn't sufficient to force the applet to automatically lay out the screen again, although it ensures that the next time the screen is redrawn, things will look okay, so you have to invoke the validate method, inherited from Component, on the applet.
The available button creator calls and the key button methods follow.
Creates a button with no label (see Figure 17.23).
Figure 17.23 : A button without a label.
Creates a button with the specified label.
Sets the button label to the specified string.
Returns the current button label as a string.
Checkboxes are text items with a checkable icon next to
them. They're generally used when you want the user to be able
to set several options prior to making a decision. You usually
don't do anything when a checkbox is checked or unchecked, you
usually just read the values of the checkboxes when some other
control, such as a button or menu item, is activated. Just in
case you do want the code to do something when a box's state changes,
checkboxes generate an ACTION_EVENT
with the new Checkbox state
as the argument after the user clicks on them.
| Note |
Radio buttons look just like checkboxes, but they are grouped and only one radio button in a group can be checked at any given time. The next section discusses how to implement radio buttons. |
The code in Listing 17.18 produces the applet interface shown in Figure 17.24.
Figure 17.24 : Checkboxes in action.
Listing 17.18. Checkboxes without a bank.
import java.awt.*;
import java.applet.Applet;
public class checkboxes extends Applet
{
public void init()
{
Checkbox box_1, box_2, box_3;
box_1 = new Checkbox();
box_2 = new Checkbox("this is a labeled checkbox");
box_3 = new Checkbox("Labeled and checked", null, true);
add(box_1);
add(box_2);
add(box_3);
}
}
The creator's methods for Checkbox and the key checkbox methods follow.
Creates a new checkbox with no label.
Creates a new checkbox with a label.
Creates a new checkbox that is labeled and checked. The middle argument is used with radio buttons.
Changes the label of a checkbox.
Returns the current label as a string.
Gets the current checkbox state (checked = TRUE).
Sets the checkbox state.
Checkboxes and radio buttons look different. Even though radio buttons are made up of checkboxes, they're called radio buttons because that's what they're called in most current GUIs. The only functional difference is that only one of the items in a radio button group can be selected at one time, like the buttons on your car radio. This is useful when you want your user to select one of a set of options. The AWT creates a radio button group by associating a CheckboxGroup instance with all the checkboxes in the group, as shown in Listing 17.19.
Listing 17.19. Creating a radio button group.
import java.awt.*;
import java.applet.Applet;
public class radio_buttons extends Applet
{
public void init()
{
CheckboxGroup group;
Checkbox box_1,box_2,box_3;
CheckboxGroup group_1;
Checkbox box_1_1,box_2_1,box_3_1;
//set up the first radio button group
group = new CheckboxGroup();
box_1 = new Checkbox("Yes", group, true);
box_2 = new Checkbox("No", group, false);
box_3 = new Checkbox("Maybe", group, false);
//set up the second group
group_1 = new CheckboxGroup();
box_1_1 = new Checkbox("Yes", group_1, false);
box_2_1 = new Checkbox("No", group_1, false);
box_3_1 = new Checkbox("Maybe", group_1, false);
//add the components to the applet panel
add(box_1);
add(box_2);
add(box_3);
add(box_1_1);
add(box_2_1);
add(box_3_1);
}
}
Figure 17.25 shows the interface to this applet. The first thing to note is that the second group doesn't have any button selected. That's because none of them were created with a checked state. As soon as the user clicks one of them, though, it won't be possible to return all of them to the unchecked state. Clicking a selected item, such as Yes in the first group, does not change its state to unchecked. Selecting one of the other buttons in the first group deselects Yes and selects the button you clicked.
Figure 17.25 : Radio buttons (nearly the same as checkboxes).
Radio buttons have only one creator method:
new Checkbox(String the_label, CheckboxGroup a_group, boolean checked?)
This creates a new Checkbox that is labeled and checked. The middle argument defines which radio button group the checkbox belongs to.
In order to use radio buttons, you also need to create a new checkbox group. Use this code:
new CheckboxGroup()
Because radio buttons are implemented as checkboxes, the methods described in the "Checkboxes" se