ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Learning Polymorphism and Object Serialization
Pages: 1, 2, 3

There are three classes that implement the Shape interface: the Rectangle class, the Oval class, and the Lineclass. You can create you own classes if you feel like playing with Brainy Draw. As a rule, all classes must override draw. The classes are explained below.



The Rectangle Class

This class represents a rectangle with two coordinates: (x1, y1), which is the top-left corner of the rectangle; and (x2, y2), which is the bottom-right corner. The class signature and body are given below.

class Rectangle implements Shape {

  int x1, y1, x2, y2;

  public Rectangle(int x1, int y1, int x2, int y2) {
    this.x1 = x1 < x2? x1 : x2;
    this.y1 = y1 < y2? y1 : y2;
    this.x2 = x1 < x2? x2 : x1;
    this.y2 = y1 < y2? y2 : y1;
  }

  public void draw(Graphics g) {
    g.setColor(Color.green);
    g.fill3DRect(x1, y1, x2 - x1, y2 - y1, true);
    //g.drawRect(x1, y1, x2 - x1, y2 - y1);
  }
} // end of class Rectangle

The constructor accepts four arguments, which are the coordinate where the user clicks the mouse (x1, y1) and the coordinate where the user releases the mouse (x2, y2). Because it is not possible to force the user to draw a rectangle by always dragging the mouse rightwards and downwards, x1 and x2 must be swapped if the mouse happens to move to the left. By the same token, y1 and y2 must be swapped if the mouse moves up.

The draw method uses the fill3DRect method of the Graphics class to draw a rectangle. An alternative, which is commented out in the code, is to use the drawRect method. The signature of the drawRect method is as follows.

public void drawRect(int x, int y, int width, int height)

The signature of the fill3DRect method is as follows.

public void fill3DRect(int x, int y, int width, int height, boolean raised)

Both methods accepts the following arguments:

  • x, the abscissa of the top-left corner coordinate of the rectangle,
  • y, the ordinate of the top-left corner coordinate of the rectangle,
  • width, the width of the rectangle,
  • height, the height of the rectangle,
  • raised, in the fill3DRect method determines whether the rectangle appears to be raised above the surface or etched into the surface

Knowing both the top-left corner and right-bottom corner coordinates allows us to calculate the width and the height of the rectangle.

Also, to make it more colorful, a green color is used to draw a rectangle, distinguishing it from other types of shapes that use different colors.

The Oval class

The Oval class represents an oval. The class signature and body are given below.

class Oval implements Shape {

  int x1, y1, x2, y2;

  public Oval(int x1, int y1, int x2, int y2) {
    this.x1 = x1 < x2? x1 : x2;
    this.y1 = y1 < y2? y1 : y2;
    this.x2 = x1 < x2? x2 : x1;
    this.y2 = y1 < y2? y2 : y1;
  }

  public void draw(Graphics g) {
    g.setColor(Color.yellow);
    g.fillOval(x1, y1, x2 - x1, y2 - y1);
    //g.drawOval(x1, y1, x2 - x1, y2 - y1);
  }
} // end of class Oval

Like that of the Rectangle class, the Oval class constructor accepts four arguments, which are the coordinate where the user clicks the mouse, (x1, y1), and the coordinate where the user releases the mouse, (x2, y2). Therefore, the same adjustments must occur if the user drags the mouse upwards or leftwards.

The draw method uses the fillOval method of the Graphics class to draw an oval. An alternative, which is commented out in the code, is to use the drawOval method. The signature of the fillOval method is as follows.

public abstract void fillOval(int x, int y, int width, int height)

The signature of the drawOval method is as follows.

public abstract void fillOval(int x, int y, int width, int height)

The arguments for both methods are as follow.

  • x, the abscissa of the top-left corner coordinate of the oval,
  • y, the ordinate of the top-left corner coordinate of the oval,
  • width, the width of the oval,
  • height, the height of the oval,

In Brainy Draw, an oval is always yellow.

The Line class

The Line class represents a straight line. The line can be drawn if the coordinates of two end points are known. The signature and body of this class are as follow.

class Line implements Shape {

  int x1, y1, x2, y2;

  public Line(int x1, int y1, int x2, int y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
  }

  public void draw(Graphics g) {
    g.setColor(Color.red);
    g.drawLine(x1, y1, x2, y2);
  }
} // end of class Line

Unlike the Rectangle and Oval classes, it is legal to draw any line with any angle within the coordinate system. Adjustments in the constructor are therefore not necessary. The draw method simply calls the drawLine method of the Graphics class. The drawLine method has the following signature.

public abstract void drawLine(int x1, int y1, int x2, int y2)

The method draws a straight line between the points (x1, y1) and (x2, y2).

This method has the following parameters.

  • x1, the first point's x coordinate
  • y1, the first point's y coordinate,
  • x2, the second point's x coordinate,
  • y2, the second point's y coordinate.

A line is drawn with the red color.

The drawing board

When drawing graphics on a Java application, one thing is always of concern. How do you draw on the Frame? In an applet, this is easy because Applet's paint method is passed a very useful parameter: an object reference of type java.awt.Graphics. Once you have the Graphics object, drawing is easy. The Graphics class has nice methods to draw many things in a color of your choice. These methods include drawArc to draw the outline of a circular or elliptical arc, drawLine to draw a line, drawPolyline to draw a sequence of connected lines, drawRect to draw a rectangle, and so on. However, with an application you have a Frame, and you can't add a Graphics object because it is not a Component. You would like to somehow initiate the abstract Graphics class and turn it into a Component. You might think of using a Panel. However, you have the same problem with a Panel as with a Frame.

There is the Canvas class, which is meant to be extended. What's more, according to Sun's documentation, "A Canvas component represents a blank rectangular area of the screen onto which the application can draw or from which the application can trap input events from the user. An application must subclass the Canvas class in order to get useful functionality such as creating a custom component. The paint method must be overridden in order to perform custom graphics on the canvas."

Sounds perfect. A Canvas even has the paint method that can perform custom graphics on its body. But, it said that a Canvas is a blank rectangular area. This has profound implication if it is to be used to draw shapes in our Brainy Draw. Everything would take a rectangular space, including an oval and a line. When you draw an oval on top of a rectangle, the corners of the oval will occupy some space. Also, if you draw a line, the space taken is not only for the dots that compose the line, but a rectangular area. And a Canvas is not hollow, so it always covers a rectangular space even though you are drawing an unfilled rectangle.

Figure 2 shows the unexpected side effects. The oval and the rectangle don't physically touch each other. However, the top-right corner of the rectangle is invisible because the rectangular area of the oval occupies it.

Figure 2.
Figure 2. The Canvas component is not a good solution for Brainy Draw.

I decided to use what had been considered the ideal candidate for the drawing board in the first place: an applet. Though it doesn't sound conventional, using an applet with a Java application is not forbidden and does not necessarily turn your application into a Web browser. As you can see later, it works as expected. The coding part is also not hard. You just need a class that extends java.applet.Applet to utilize its paint method. Once you have a Graphics object that comes free with the paint method, you can use its methods to do a lot of drawing. Now, instead of a drawing panel that is an object of Panel, you have a panel that's a subclass of Applet. Adding the applet to the Frame is also very simple using the add(Component c) method because Applet is derived from Component.

The signature and body of the PanelApplet class, the new class that you use as a drawing board, is given below.

import java.applet.Applet;
import java.util.*;

class PanelApplet extends Applet {

  Vector shapes = new Vector();

  public void paint(Graphics g) {
    Enumeration e = shapes.elements();
    while (e.hasMoreElements()) {
      Shape s = (Shape) e.nextElement();
      s.draw(g);
    }
  }
} // end of PanelApplet class

Vector shapes is a container for all the objects drawn (rectangles, ovals, and lines). An element will be added when the user draws a shape and removed when the user clicks Undo. To display these objects correctly on the screen, you need to tell the paint method to loop through the Vector and draws the elements in the correct order.

The most interesting part of the overridden paint method are the following lines.

Shape s = (Shape) e.nextElement();
s.draw(g);

Each element (which is a Rectangle object, an Oval object, or a Line object) is upcast to a Shape object; and without having to know whether the Shape object is actually a Rectangle, an Oval, or a Line object, you can call the draw method. All because, and this is the magic of polymorphism, the JVM knows which draw method to call. If the Shape object is a Rectangle, the draw method in the Rectangle class is called. If it is a Line object, Java will invoke the draw method in the Line class. As a result, each object will be correctly drawn by the paint method.

Pages: 1, 2, 3

Next Pagearrow