3.3.1 The BlinkingObjects Example

Below we shall extend our letter applet of the previous section to blinking characters, blinking rectangles, and blinking ellipses. Below we do not give the complete Java code because the part about blinking involves multithreading and double buffering of graphics. These two topics will be discussed later. The interested or curious reader can have a look at all source files. Here, we shall only show the implementation of the interfaces and of the parts of the Java code for blinking characters that are relevant for the hierarchy. Blinking shapes such as circles and ellipses are handled similarly. The picture showing the relationships between classes is as follows:

Table of Contents

  1. The Applet
  2. The Interfaces Drawable and Blinkable
  3. ScreenCharacter.java
  4. BlinkingCharacter.java
  5. BlinkingLetter.java
  6. BlinkingDigit.java
  7. BlinkingObjectsApplet.java
The Applet
The Interfaces Drawable and Blinkable
import java.awt.Graphics;

interface Drawable {
  void draw (Graphics g);
}

interface Blinkable extends Runnable, Drawable {

  float MAXRATE = 10;   // maximum blinking rate is 10 flashes per second

  float MINRATE = 0.1f; // minimum blinking rate is 1 flash per 10 seconds 

  void  startBlinking();

  void  stopBlinking();

  void  setBlinkingRate(float r);

  float getBlinkingRate();  
}
ScreenCharacter.java
import java.awt.*;

class ScreenCharacter implements Drawable {
  ...

  public void draw(Graphics g) {
    ...
  }
}
The definition of the class is the same as before.
BlinkingCharacter.java
import java.applet.Applet;
import java.awt.*;

class BlinkingCharacter extends ScreenCharacter implements Blinkable {

  Applet applet;
  float blinkingRate = MAXRATE;
  int laptime = (int) (1/blinkingRate);
  private Thread blinker = null;
  private boolean isVisibleQ = true;

  BlinkingCharacter(Applet applet) {   
    // constructor for default initialization
    this("a", 12, 0, 0, applet);
  }

  BlinkingCharacter(String name, int fontsize, int x, int y, Applet applet) { 
    // constructor to initialize letter and position
    super(name, fontsize, x, y);
    this.applet = applet;
  }
 
  void randomize() {
    int laptime = randomNumber(500,2000);
    setBlinkingRate((float) 1/laptime);
    super.randomize();
  }

  public void draw(Graphics g) {
    if (isVisibleQ) {
      super.draw(g);
    }
  }

  public void run () {
    while (blinker != null) {
      applet.repaint();
      ...
      isVisibleQ = !isVisibleQ;
    }
  }

  public void startBlinking () {
    ...
  }

  public void stopBlinking () {
    ...
  }

  public float getBlinkingRate () {
    return blinkingRate;
  }

  public void setBlinkingRate (float r) {
    blinkingRate = Math.max(MINRATE, Math.min(r, MAXRATE));
  }
}
BlinkingLetter.java
import java.applet.Applet;
import java.awt.*;

class BlinkingLetter extends BlinkingCharacter {
  String letterCase;

  BlinkingLetter (Applet applet) {
    this("a", 12, 0, 0, "lowercase", applet);
  }

  BlinkingLetter (String name, int fontSize, int x, int y, 
                  String letterCase, Applet applet) {
    super(name, fontSize, x, y, applet);
    setCase(letterCase);
  }

  void toLowercase() {
    letterCase = "lowercase";
    name = name.toLowerCase();
  }

  void toUppercase() {
    letterCase = "uppercase";
    name = name.toUpperCase();
  }

  void randomize() {
    // random name
    int i = randomNumber(0,25);
    name = "abcdefghijklmnopqrstuvwxyz".substring(i,i+1);
    // random case
    if (randomNumber(0,1) == 0) {
      toLowercase();
    }
    else {
      toUppercase();
    }
    // random other properties
    super.randomize();
  }
}
BlinkingDigit.java
import java.applet.Applet;
import java.awt.*;

class BlinkingDigit extends BlinkingCharacter {

  BlinkingDigit (Applet applet) {
    this("1", 12, 0, 0, applet);
  }

  BlinkingDigit(String name, int fontsize, int x, int y, 
                 Applet applet) {
    super(name, fontsize, x, y, applet);
  }

  void randomize() {
    // random digit
    int i = randomNumber(0,9);
    name = "0123456789".substring(i,i+1);
    // random other properties
    super.randomize();
  }
}
BlinkingObjectsApplet.java
import java.applet.Applet;
import java.awt.*;
import java.util.*;

public class BlinkingObjectsApplet extends Applet {

  Vector objects = new Vector();
  ...

  public void init() {
    add(new Button("new object"));
    newObject(this);
  }

  public void start() {
    Enumeration e = objects.elements(); 
    Blinkable obj;
    while (e.hasMoreElements()) { // step through all vector elements
      obj = (Blinkable) e.nextElement();
      obj.startBlinking();
    }
  }

  public void stop() {
    Enumeration e = objects.elements();
    Blinkable obj; 
    while (e.hasMoreElements()) { // step through all vector elements
      obj = (Blinkable) e.nextElement();
      obj.stopBlinking();
    }
  }

  public boolean action(Event e, Object arg) {
    newObject(this);
    repaint(); 
    return(true);
  }

  public void paint(Graphics g) {
    update(g);
  }

  public void update(Graphics g) {
    ...
    Enumeration e = objects.elements(); 
    Blinkable obj;
    while (e.hasMoreElements()) { // step through all vector elements
       obj = (Blinkable) e.nextElement();
       obj.draw(offGraphics);
    } 
    //Paint the image onto the screen.
    g.drawImage(offImage, 0, 0, this);
  }

  void newObject(Applet applet) {
    BlinkingCharacter c;
    BlinkingShape s;
    if (0 == (int) Math.round(Math.random())) {
    // select character
      if (0 == (int) Math.round(Math.random())) {
        c = new BlinkingLetter(applet);
      }
      else {
        c = new BlinkingDigit(applet);
      }
      c.randomize(); // choose random properties of character
      c.startBlinking();
      objects.addElement(c);
    }
    else {
    // select shape
      if (0 == (int) Math.round(Math.random())) {
        s = new BlinkingOval(applet);
      }
      else {
        s = new BlinkingRectangle(applet);
      }
      s.randomize(); // choose random properties of shape
      s.startBlinking();
      objects.addElement(s);
    }
  }   
}  
In the source code, in the methods start, stop, and update, you can see that we step each time through the vector of blinkable objects. Let us look more closely at the code for the start method
    Enumeration e = objects.elements(); 
    Blinkable obj;
    while (e.hasMoreElements()) { // step through all vector elements
       obj = (Blinkable) e.nextElement();
       obj.draw(offGraphics);
    } 
In the first line, all elements of the vector object are placed in an Enumeration object. This is a container for objects, and here we mean literally instances of the Object class. We want to specify that the objects present in this container have the property of being Blinkable. We declare the Blinkable object obj so that whenever we select an element in the container, we can cast its type to Blinkable, assign it to the variable obj and we can send messages of the Blinkable interface to this object without knowing whether it is a blinking character, blinking oval shape, or a blinking rectangle.