Chapter 17

Programming the User Interface


CONTENTS


Introduction and Basic Concepts

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.

Platform Independence: Gone Today, Here Tomorrow

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.

Abstract Window Toolkit (AWT)

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.2 : All the classes that control the placement of objects on the screen inherit from the Java.lang object.class.

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.

Graphics Class

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.

clipRect(int x, int y, int width, int height)
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.
copyArea(int x, int y, int width, int height, int new_x, int new_y)
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.
Graphics, create()
Makes a clone of the Graphics object.
Graphics, create(int x, int y, int width, int height)
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.
setPaintMode()
Sets the painting mode to draw over; it is used to undo the effects of setXORMode.
setXORMode(Color a_color)
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.
translate(int x, int y)
Translates the origin of the Graphics area to the specified point.

The update, paint, and repaint Methods

You'll encounter three key methods over and over again as you work with the various user interface elements.

repaint
Requests a redraw of an item or an entire interface. It then calls update.
update
Controls what happens when repaint is called; you can override this method.
paint
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.

repaint(long time_in_milliseconds)
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.
repaint(int x, int y, int width, int height)
Repaints only the rectangular part of the screen defined by x, y, width, and height.
repaint(long time, int x, int y, int width, int 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.

Working with Color

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.

RGB and HSB Color Formats

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,
means that the clipRect method does not return a value.
Graphics, create()
means that the create method returns a value of type Graphics.
Color, brighter()
Returns a new color that is approximately 1.5 times brighter than the current color.
Color, darker()
Returns a color about 70 percent as bright as the original color.
boolean, equals(Object item)
Checks to see whether two colors are the same.
int, getBlue()
Returns the blue component of a color.
Color, getColor(String 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.
Color, getColor(String color, Color default)
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.
Color, getColor(String color, int rgb)
Performs the same function as the preceding, except you supply a default packed integer with RGB values, not an instance of color.
Color, getHSBColor(float hue, float saturation, float brightness)
Returns an instance of the Color class based on the supplied HSB values (hue, saturation, and brightness). This is a static method.
int, getGreen()
Returns the green component of the color.
int, HSBtoRGB(float hue, float saturation, float brightness)
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.
int, getRGB()
Returns the packed-integer version of the RGB values of a color.
int, getRed()
Returns the red component of a color.
float[], RGBtoHSB(int r, int g, int b)
Returns an array of floats containing the h, s, and b values of the equivalent color to the specified r, g, and b values.

Using HSB Colors

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.

Figure 17.6 : A Java rainbow brought to you by the wonders of HSB. Because you easily can create color objects with RGB or HSB values, you can use both in any program.


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);
        }
    }
}

Geometric Classes

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.

Dimension

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;

Point

Represents an x and y coordinate. The points x and y are accessible. Point also supports two methods.

move(int x, int y)
Sets the x and y values of the point to the input parameter values.
translate(int delta_x, int delta_y)
Adds the input parameter values to the current x and y values of the point-for example, x = x + delta_x.

Polygon

Represents an arbitrarily ordered list of vertices. You can directly access the three main attributes.

int x[]
Specifies the array of x values for the vertices.
int y[]
Specifies the array of y values. The nth y value and the nth x value define a vertex location.
Polygon has several useful methods.
int npoints
Specifies the number of vertices.
addPoint(int x, int y)
Adds a new vertex to the polygon.
Rectangle, getBoundingBox()
Returns the smallest rectangle that contains all the vertices.
boolean, inside(int x, int y)
Returns TRUE if the point defined by x and y is inside the polygon.

Rectangle

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.

add(int x, int y)
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.
add(Point a_point)
Performs the same function as the preceding method, except the input parameter is a point rather than two integers.
add(Rectangle a_rectangle)
Grows the rectangle to the smallest one that contains the original rectangle and the rectangle supplied as the input parameter.
grow(int delta_w, int delta_h)
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.
boolean, inside(int x, int y)
Returns TRUE if the point x,y is inside the rectangle.
Rectangle, intersection(Rectangle rect)
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.
boolean, intersects(Rectangle rect)
Returns TRUE if the rectangles overlap, including just a shared edge or vertex.
boolean, isEmpty()
Returns TRUE if the height or width of a rectangle is 0.
move(int x, int y)
Sets the origin of the rectangle to x,y.
reshape(int x, int y, int width, int height)
Sets the origin of the rectangle to x,y, its width to width, and its height to height.
resize(int width, int height)
Sets the width of the rectangle to width and its height to height.
translate(int d_x, int d_y)
Moves the rectangle a distance d_x in x and d_y in y.
Rectangle, union(Rectangle rect)
Returns a new rectangle that is the smallest one that contains both rectangles.

The Toolkit

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.

Dimension: getScreenSize()
Returns the screen size in pixels.
int: getScreenResolution()
Returns the screen resolution in pixels/inch.
ColorModel: getColorModel()
Returns the ColorModel, which defines how color is represented on the platform.
String[]: getFontList()
Gets a list of the names of the fonts available on the platform.
FontMetrics: getFontMetrics(Font font)
Returns the FontMetrics, which provide measurements of a font's screen size, for the specified font on the desktop.
Image: getImage(String filename)
Gets an image from the file specified in the input argument.
Image: getImage(URL a_url)
Gets an image from a specified URL.
boolean, prepareImage(Image an_image, int width, int height, ImageObserver obs)
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.
int, checkImage(Image an_image, int width, int height, ImageObserver obs)
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.
Image: createImage(ImageProducer an_image_source)
Takes an image source, such as a filter, and creates an image.

Drawing

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.

Drawing Text

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.

Working with Fonts

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.

Measuring Fonts, Centering Text

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.

Table 17.1. FontMetrics terms.

HeightThe 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.
BaselineThe 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.
AscentMeasures the height of the character above the baseline. This can include the amount of whitespace recommended by the font designer.
DescentMeasures 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.

Drawing Lines

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.

drawLine(int x_start, int y_start, int x_end, int y_end)
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
drawArc(int x, int y, int width, int height, int start_angle, int delta_angle)
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.
fillArc(int x, int y, int width, int height, int start_angle, int delta_angle)
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.

Drawing Shapes

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.

drawRect(int x, int y, int width, int height)
This draws a traditional rectangle.
x: Upper-left corner
y: Upper-left corner
width: Width of the rectangle
height: Height of the rectangle
drawRoundRect(int x, int y, int width, int height, int r_width, int r_height)
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
draw3DRect(int x, int y, int width, int height, boolean flag)
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
drawPolygon(int x[], int y[], int n_points)
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
drawOval(int x, int y, int width, int height)
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.

Filling Shapes

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.

Animation

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);
    }
}

Interactive Interface Elements

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).

Component Class-Shared Features of All Active GUI Elements

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.

boolean, action(Event e, Object o)
This method usually is overridden. It's called whenever an ACTION_EVENT occurs on a component. Events are discussed in Chapter 21, "Event Handling."
boolean, keyDown(Event e, int key)
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.
boolean, keyUp(Event e, int key)
This method is invoked when the component receives a KEY_RELEASE event.
boolean, lostFocus(Event e, Object o)
This is called when the object receives a LOST_FOCUS event.
boolean, mouseDown(Event e, int x, int y)
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.
boolean, mouseDrag(Event e, int x, int y)
This is invoked when the user drags the mouse with the mouse button down over the component, generating a MOUSE_DRAG event.
boolean, mouseEnter(Event e, int x, int y)
This is invoked each time the mouse goes over the component, generating a MOUSE_ENTER event.
boolean, mouseExit(Event e, int x, int y)
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.
Rectangle, bounds()
Returns the bounding rectangle that contains the component.
int, checkImage(Image img, ImageObserver iobs)
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.
Image, createImage(int width, int height)
Creates a new Image of the specified size.
disable()
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.
enable()
Enables a disabled component. This is a synchronized method.
Color, getBackground()
Returns the color of the background for the component.
Font, getFont()
Returns the current font for the component.
FontMetrics, getFontMetrics()
Gets the FontMetrics, which contains information about the size of text on the current platform, for the component.
Color, getForeground()
Returns the foreground color-the one that will be used to draw lines, fill shapes, and so on.
Graphics, getGraphics()
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.
hide()
Makes the component invisible. This is a synchronized method.
boolean, inside(int x, int y)
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.
invalidate()
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.
boolean, isEnabled()
Returns TRUE if the component is enabled to respond to user actions.
boolean, isShowing()
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.
boolean, isVisible()
Returns TRUE if the component currently is visible. You can make a component invisible by invoking its hide method.
Point, location()
Returns a point that contains the coordinates of the component's origin.
move(int x, int y)
Moves the component to the specified position in the parent container's coordinate system.
paint(Graphics g)
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.
boolean, prepareImage(Image img, ImageObserver img_obs)
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.
repaint(long time)
Repaints this component by a specified time or cancels the request.
repaint(int x, int y, int width, int height)
Repaints the specified part of the component.
repaint(long time, int x, int y, int width, int height)
Tries to repaint the specified region. If it can't do so before the specified time, it quits.
reshape(int x, int y, int width, int height)
Enables you to specify the position and size of the component. This is a synchronized method.
resize(int width, int height)
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.
resize(Dimension dim)
Scales the component to fit in the defined bounding rectangle maintaining the same origin.
setBackground(Color a_color)
Sets the background color for a component. This is a synchronized method.
setFont(Font a_font)
Specifies the font that will be used for any text drawn in the component. This is a synchronized method.
setForeground(Color a_color)
Sets the color used for drawing lines and filling in shapes. This is a synchronized method.
show()
Makes the component visible if it had been hidden.
Dimension, size()
Returns the height and width of the component.
update(Graphics g)
Erases the contents of the component's graphic area every time it's called.
validate()
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.

Containers

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.

add(Component a_component)
Adds a component to the container.
add(Component a_component, int pos)
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.
int, countComponents()
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.
Component, getComponent(int index)
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.
Component[], getComponents()
Returns an array of references to all the components in the container. This is a synchronized method.
insets, insets()
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.
remove(Component a_component)
Removes the component from the container. This is a synchronized method.
setLayout(LayoutManager lm)
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.

Panels

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.

Insets

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.

Frame

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.

Windows

This class isn't used very often, but it's a top-level window without borders and a menu bar.

Labels

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:

new Label(String label)
Produces a label with the specified string.
new Label(String label,int positioning)
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.
String, getText()
Returns the label string.
setText(String new_label)
Changes the label text.

Buttons

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.

new Button()
Creates a button with no label (see Figure 17.23).

Figure 17.23 : A button without a label.

new Button(String the_button_label)
Creates a button with the specified label.
setLabel(String the_new_label)
Sets the button label to the specified string.
String getLabel()
Returns the current button label as a string.

Checkboxes

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.

new Checkbox()
Creates a new checkbox with no label.
new Checkbox(String the_label)
Creates a new checkbox with a label.
new Checkbox(String the_label, CheckboxGroup null, boolean checked?)
Creates a new checkbox that is labeled and checked. The middle argument is used with radio buttons.
setLabel(String the_new_label)
Changes the label of a checkbox.
String getLabel()
Returns the current label as a string.
boolean getState()
Gets the current checkbox state (checked = TRUE).
setState(boolean new_state)
Sets the checkbox state.

Radio Buttons

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" section are the ones you'll use