Using the Graphics class

A close look at the Graphics class and the drawing primitives it provides, and a demonstration of its use

A variety of factors inspire people to write software programs. I believe that for many, motivation springs from a desire to create graphics, to manipulate images, or to animate. Whether they want to create arcade games, flight simulators, or CAD packages, developers often begin by learning to draw.

The graphics toolbox within the Abstract Windowing Toolkit (or AWT) makes it possible for a Java programmer to draw simple geometric shapes, print text, and position images within the borders of a component, such as a frame, panel, or canvas.

This column is my first on the topic of graphics. It will focus on the Graphics class and its methods for drawing simple geometric shapes, and will introduce the process by which painting (and repainting) occurs.

Let's begin at center stage -- the Graphics class.

The Graphics class

It's essential that programmers understand the Graphics class before they attempt to draw images via Java. The Graphics class provides the framework for all graphics operations within the AWT. It plays two different, but related, roles. First, it is the graphics context. The graphics context is information that will affect drawing operations. This includes the background and foreground colors, the font, and the location and dimensions of the clipping rectangle (the region of a component in which graphics can be drawn). It even includes information about the eventual destination of the graphics operations themselves (screen or image). Second, the Graphics class provides methods for drawing simple geometric shapes, text, and images to the graphics destination. All output to the graphics destination occurs via an invocation of one of these methods.

In order to draw, a program requires a valid graphics context (represented by an instance of the Graphics class). Because the Graphics class is an abstract base class, it cannot be instantiated directly. An instance is typically created by a component, and handed to the program as an argument to a component's update() and paint() methods. These two methods, along with the repaint() method, are discussed in the next section.

The methods

The following three methods are involved in displaying graphics. Default versions of each are provided by class Component. Methods update() and paint() should be redefined to perform the desired graphics operations.

repaint()

public void repaint() public void repaint(long tm) public void repaint(int x, int y, int w, int h) public void repaint(long tm, int x, int y, int w, int h)

The repaint() method requests that a component be repainted. The caller may request that repainting occur as soon as possible, or may specify a period of time in milliseconds. If a period of time is specified, the painting operation will occur before the period of time elapses. The caller may also specify that only a portion of a component be repainted. This technique is useful if the paint operation is time-consuming, and only a portion of the display needs repainting. The code in Listing 1 illustrates how the repaint() method might be used in a program.

boolean mouseDown(Event e, int x, int y) { selected_object.move(x, y); repaint(); }

Listing 1: Mouse-down event handler

The code in the mouseDown() event handler recalculates the position of an object in a display based on the position of the mouse and calls the repaint() method to indicate that the display should be repainted as soon as possible.

update()

public void update(Graphics g)

The update() method is called in response to a repaint() request, or in response to a portion of the component being uncovered or displayed for the first time. The method's sole argument is an instance of the Graphics class. The Graphics instance is valid only within the context of the update() method (and any methods it calls), but is disposed of soon after the update() method returns. The default implementation provided by the Component class erases the background and calls the paint() method (below).

paint()

public void paint(Graphics g)
The paint() method is called from an update() method, and is responsible for actually drawing the graphics.  The method's sole argument is an instance of the Graphics class.  The default implementation provided by class Component does nothing.

How components are repainted

To reduce the time required to repaint the display, the AWT takes two shortcuts:

  • First, the AWT repaints only those components that need repainting, either because they have been uncovered, or because they asked to be repainted.

  • Second, if a component was covered and is uncovered, the AWT repaints only the portion of the component that was previously covered.

The applet in Figure 1 allows you to observe this process as it occurs. Ignore the text area at the top of the applet for a moment, and watch only the colored portion of the display. Using another window, momentarily cover and then uncover part of the applet. Notice that only the portion of the applet that was covered is repainted. Furthermore, only those components that were covered are repainted, no matter what their position in the component hierarchy. By deliberately using different colors, the applet makes this subtle effect noticeable. The source code for this figure is available here.

Figure 1: Repaint browser

The Graphics coordinate system

The methods described in the following section take, as parameters, values that specify how a shape is to be drawn. For example, the drawLine() method expects four parameters. The first two parameters specify the location of the beginning of the line, and the last two parameters specify the location of the end of the line. The exact values to be passed to the drawLine() method are determined by the coordinate system in effect.

A coordinate system is a method for unambiguously specifying the location of points in space. In the case of the AWT, this space is a two-dimensional surface called a plane. Each location in a plane can be specified by two integers, called the x and y coordinates. The values of the x and y coordinates are calculated in terms of the point's respective horizontal and vertical displacement from the origin. In the case of the AWT, the origin is always the point in the upper-left corner of the plane. It has the coordinate values 0 (for x) and 0 (for y). The illustration in Figure 2 shows two points -- one located at the origin, and another located at a position seven across and five down from the origin.

Figure 2: The coordinate plane

The Graphics primitives

This section introduces methods for drawing lines, rectangles, ovals and arcs, and polygons. Since these methods work only when invoked on a valid Graphics instance, they may be used only within the scope of a component's update() and paint() methods. Most of the methods that follow come in pairs. One method (the drawX() method) draws only the outline of the specified shape, and the other method (the fillX() method) draws a filled version of the specified shape.

lines

void drawLine(int xBegin, int yBegin, int xEnd, int yEnd)

This is the simplest of all graphics methods. It draws a straight line, a single pixel wide, between the specified beginning and ending points. The resulting line will be clipped to fit within the boundaries of the current clipping region. The line will be drawn in the current foreground color.

The applet in Figure 3 demonstrates the drawLine() method in action. The source code is available here. This applet and the applets in Figures 4, 6, and 7 require the services of two support classes: the NewCanvas class, and the Figure interface. The NewCanvas class extends class Canvas, and provides a specialized drawing surface for figures. The source code for the NewCanvas class is available here. The Figure interface defines the methods a figure must provide in order to be used with NewCanvas. The source code for the Figure interface is available here.

Figure 3: Line drawing demonstration

rectangles
void drawRect(int x, int y, int w, int h) void fillRect(int x, int y, int w, int h) void drawRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight) void fillRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight) void draw3DRect(int x, int y, int w, int h, boolean raised) void fill3DRect(int x, int y, int w, int h, boolean raised)

Each of these graphics methods require, as parameters, the x and y coordinates at which to begin the rectangle, and the width and the height of the rectangle. Both the width and the height must be positive integers. The resulting rectangle will be clipped to fit within the boundaries of the current clipping region. The rectangle will be drawn in the current foreground color. Rectangles come in three different styles: plain, with rounded corners, and with a slight (but often hard-to-see) three-dimensional effect.

The rounded-rectangle graphics methods require two additional parameters, an arc width and an arc height, both of which control the rounding of the corners. The three-dimensional rectangle methods require an additional parameter that indicates whether or not the rectangle should be sunken or raised.

The applet in Figure 4 demonstrates these methods in action. The source code is available here.

Figure 4: Rectangle drawing demonstration

ovals and arcs

void drawOval(int x, int y, int w, int h) void fillOval(int x, int y, int w, int h) void drawArc(int x, int y, int w, int h, int startAngle, int arcAngle) void fillArc(int x, int y, int w, int h, int startAngle, int arcAngle)

Each of these graphics methods require, as parameters, the x and y coordinates of the center of the oval or arc, and the width and height of the oval or arc. Both the width and the height must be positive integers. The resulting shape will be clipped to fit within the boundaries of the current clipping region. The shape will be drawn in the current foreground color.

The arc graphics methods require two additional parameters, a start angle and an arc angle, to specify the beginning of the arc and the size of the arc in degrees (not radians). Figure 5 illustrates how angles are specified.

Figure 5: Angle specification

The applet in Figure 6 demonstrates these methods in action. The source code is available here.

Figure 6: Oval and arc drawing demonstration

polygons

void drawPolygon(int xPoints[], int yPoints[], int nPoints) void drawPolygon(Polygon p) void fillPolygon(int xPoints[], int yPoints[], int nPoints) void fillPolygon(Polygon p)

Polygons are shapes formed from a sequence of line segments. Each of the polygon graphics methods require, as parameters, the coordinates of the endpoints of the line segments that make up the polygon. These endpoints can be specified in either one of two ways: as two parallel arrays of integers, one representing the successive x coordinates and the other representing the successive y coordinates; or with an instance of the Polygon class. The Polygon class provides the method addPoint(), which allows a polygon definition to be assembled point by point. The resulting shape will be clipped to fit within the boundaries of the current clipping region.

The applet in Figure 7 demonstrates these methods in action. The source code is available here.

Figure 7: Polygon drawing demonstration

Conclusion

Believe it or not, these few simple graphics primitives, combined with all we've covered in the last several months (the AWT, event handling, observers, etc.) are all that you need to write a heap of useful applications, ranging from games to CAD systems. Next month, I'll put all of these bits and pieces together and show you what I mean.

Stay tuned.

Todd Sundsted has been writing programs since computers became available in desktop models. Though originally interested in building distributed object applications in C++, Todd moved to the Java programming language when Java became the obvious choice for that sort of thing. In addition to writing, Todd provides Internet and Web consulting services to companies in the southeastern United States. :END_BIO

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies