Previous | Next | Trail Map | Creating a User Interface | Working with Graphics


Drawing Shapes

The Graphics(in the API reference documentation) class defines methods for drawing the following kinds of shapes: Except for polygons and lines, all shapes are specified using their bounding rectangle. Once you understand rectangles, drawing other shapes is relatively easy. For this reason, this page concentrates on rectangle drawing.

Example 1: Simple Rectangle Drawing

The applet on the previous page used the draw3DRect() and fillRect() methods to draw its interface. Here's the applet again:

Here's its code. Below is just the drawing code:

//In FramedArea (a Panel subclass):
public void paint(Graphics g) {
    Dimension d = size();
    Color bg = getBackground();
 
    //Draw a fancy frame around the applet.
    g.setColor(bg);
    g.draw3DRect(0, 0, d.width - 1, d.height - 1, true);
    g.draw3DRect(3, 3, d.width - 7, d.height - 7, false);
}

//In CoordinateArea (a Canvas subclass):
public void paint(Graphics g) {
    //If user has clicked, paint a tiny rectangle where click occurred
    if (point != null) {
        g.fillRect(point.x - 1, point.y - 1, 2, 2);
    }
}
The applet creates (and contains) a FramedArea object, which in turn creates (and contains) a CoordinateArea object. The first call to draw3DRect() creates a rectangle as big as the FramedArea's drawing area. The true argument specifies that the rectangle should appear to be raised. The second call to draw3DRect() creates a second rectangle just a bit smaller, with false specifying that the rectangle should appear to be sunken. Together, the two calls produce the effect of a raised frame that contains the CoordinateArea. (FramedArea implements the insets() method so that the CoordinateArea's drawing area is a few pixels inside of the FramedArea.)

The CoordinateArea uses fillRect() to draw a two-by-two-pixel rectangle at the point that the user clicks.

Example 2: Using a Rectangle to Indicate a Selected Area

Here's an applet that you could use as a basis for implementing selection in a drawing program. When the user drags the mouse, the applet continuously displays a rectangle. The rectangle starts at the cursor position where the user first pressed the mouse button and ends at the current cursor position.

Here's the applet's code. Below is the significant new code:

class SelectionArea extends Canvas {
    . . .

    public boolean mouseDown(Event event, int x, int y) {
        currentRect = new Rectangle(x, y, 0, 0);
        repaint();
        return false;
    }
    
    public boolean mouseDrag(Event event, int x, int y) {
        currentRect.resize(x - currentRect.x, y - currentRect.y);
        repaint();
        return false;
    }
    
    public boolean mouseUp(Event event, int x, int y) {
        currentRect.resize(x - currentRect.x, y - currentRect.y);
        repaint();
        return false;
    }
    
    public void paint(Graphics g) {
        Dimension d = size();
    
        //If currentRect exists, paint a rectangle on top.
        if (currentRect != null) {
            Rectangle box = getDrawableRect(currentRect, d);
            controller.rectChanged(box);
        
            //Draw the box outline.
            g.drawRect(box.x, box.y, box.width - 1, box.height - 1);
        }
    }
    
    Rectangle getDrawableRect(Rectangle originalRect, Dimension drawingArea) {
        . . .
        //Make sure rectangle width and height are positive.
        . . .
        //The rectangle shouldn't extend past the drawing area.
        . . .
    }
}
As you can see, the SelectionArea keeps track of the currently selected rectangle, using a Rectangle object called currentRect. As implemented, the currentRect keeps the same origin (currentRect.x, currentRect.y) for as long as the user drags the mouse. This means that the height and width of the rectangle can be negative.

However, the drawXxx() and fillXxx() methods don't draw anything if either the height or width is negative. For this reason, when the SelectionArea draws the rectangle, it must specify the upper left vertex of the rectangle so that the width and height are positive. The SelectionArea class defines a getDrawableRect() method to perform the necessary calculations to find the upper left vertex. The getDrawableRect() method also makes sure that the rectangle doesn't extend beyond the boundaries of its drawing area. Here, again is a link to the source code. You'll find the definition of getDrawableRect() at the bottom of the file.

Note: It's perfectly legal to specify x, y, height, or width values that are negative or cause a result larger than the drawing area. Values outside the drawing area don't matter too much because they're clipped to the drawing area. You just won't see part of the shape. Negative height or width results in the shape not being drawn at all.

Example 3: A Shape Sampler

The following applet demonstrates all the shapes you can draw and fill.

Unless your applet viewer's default font is very small, the text displayed in the above applet might look ugly in places. Words might be drawn on top of each other. And because this applet doesn't use the insets() method to protect its boundaries, text might be drawn on top of the frame around the applet. The next page improves on this example, teaching you how to make sure text fits within a given space.

Here's the code for the shape-drawing applet. Here's just the code that draws the geometric shapes. The rectHeight and rectWidth variables specify the size in pixels of the area each shape must be drawn in. The x and y variables are changed for every shape, so that the shapes aren't drawn on top of each other.

Color bg = getBackground();
Color fg = getForeground();
. . .

// drawLine() 
g.drawLine(x, y+rectHeight-1, x + rectWidth, y); // x1, y1, x2, y2
. . .

// drawRect() 
g.drawRect(x, y, rectWidth, rectHeight); // x, y, width, height
. . .

// draw3DRect() 
g.setColor(bg);
g.draw3DRect(x, y, rectWidth, rectHeight, true);
g.setColor(fg);
. . .

// drawRoundRect() 
g.drawRoundRect(x, y, rectWidth, rectHeight, 10, 10); // x, y, w, h, arcw, arch
. . .

// drawOval() 
g.drawOval(x, y, rectWidth, rectHeight); // x, y, w, h
. . .

// drawArc() 
g.drawArc(x, y, rectWidth, rectHeight, 90, 135); // x, y, w, h
. . .

// drawPolygon() 
Polygon polygon = new Polygon();
polygon.addPoint(x, y);
polygon.addPoint(x+rectWidth, y+rectHeight);
polygon.addPoint(x, y+rectHeight);
polygon.addPoint(x+rectWidth, y);
//polygon.addPoint(x, y); //don't complete; fill will, draw won't
g.drawPolygon(polygon); 
. . .

// fillRect() 
g.fillRect(x, y, rectWidth, rectHeight); // x, y, width, height
. . .

// fill3DRect() 
g.setColor(bg);
g.fill3DRect(x, y, rectWidth, rectHeight, true);
g.setColor(fg);
. . .

// fillRoundRect() 
g.fillRoundRect(x, y, rectWidth, rectHeight, 10, 10); // x, y, w, h, arcw, arch
. . .

// fillOval() 
g.fillOval(x, y, rectWidth, rectHeight); // x, y, w, h
. . .

// fillArc() 
g.fillArc(x, y, rectWidth, rectHeight, 90, 135); // x, y, w, h
. . .

// fillPolygon() 
Polygon filledPolygon = new Polygon();
filledPolygon.addPoint(x, y);
filledPolygon.addPoint(x+rectWidth, y+rectHeight);
filledPolygon.addPoint(x, y+rectHeight);
filledPolygon.addPoint(x+rectWidth, y);
//filledPolygon.addPoint(x, y);
g.fillPolygon(filledPolygon); 


Previous | Next | Trail Map | Creating a User Interface | Working with Graphics