计时器也就是秒表,本代码做为范例出现,是因为它是一个时钟型计时器,一方面使用了CANVAS来描绘我们的时钟。1代码2学习。
*--------------------------------------------------
* AnimatedTimer - Main midlet.
* Shows canvas with an animated timer. Includes
* configuration options to start/stop the timer
* and to adjust the sleep interval of the thread
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.com
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/

import java.util.Stack;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDlet;

public class AnimatedTimer extends MIDlet {
  private Display display; // The display

  protected TimerCanvas cvTimer; // Canvas to display timer

  protected OptionsList lsOptions; // List to change timer options

  protected SleepForm fmSleep; // Form with gauge to set timer sleep

  protected DisplayManager displayMgr; // Class to help manage screens

  public AnimatedTimer() {
    display = Display.getDisplay(this);
    cvTimer = new TimerCanvas(this);
    lsOptions = new OptionsList("Timer options", List.IMPLICIT, this);
    fmSleep = new SleepForm("Adjust sleep", this);

    // Create a display manager object
    displayMgr = new DisplayManager(display, cvTimer);
  }

  protected void startApp() {
    // Start with the canvas
    display.setCurrent(cvTimer);
  }

  protected void pauseApp() {
  }

  protected void destroyApp(boolean unconditional) {
  }

  public void exitMIDlet() {
    destroyApp(true);
    notifyDestroyed();
  }
}

/*--------------------------------------------------
* Use a stack to push and pop displayable objects
*
* public void pushDisplayable(Displayable)
* public void popDisplayable()
* public void home()  
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.com
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/

class DisplayManager extends Stack {
  private Display display; // Reference to Display object

  private Displayable mainDisplayable; // Main displayable for MIDlet

  private Alert alStackError; // Alert for error conditions

  /*--------------------------------------------------
   * Display manager constructor
   *-------------------------------------------------*/
  public DisplayManager(Display display, Displayable mainDisplayable) {
    // Only one display object per midlet, this is it
    this.display = display;
    this.mainDisplayable = mainDisplayable;

    // Create an alert displayed when an error occurs
    alStackError = new Alert("Displayable Stack Error");
    alStackError.setTimeout(Alert.FOREVER); // Modal
  }

  /*--------------------------------------------------
   * Push the current displayable onto stack and set
   * the passed in displayable as active
   *-------------------------------------------------*/
  public void pushDisplayable(Displayable newDisplayable) {
    push(display.getCurrent());
    display.setCurrent(newDisplayable);
  }

  /*--------------------------------------------------
   * Return to the main displayable object of MIDlet
   *-------------------------------------------------*/
  public void home() {
    while (elementCount > 1)
      pop();
    display.setCurrent(mainDisplayable);
  }

  /*--------------------------------------------------
   * Pop displayable from stack and set as active
   *-------------------------------------------------*/
  public void popDisplayable() {
    // If the stack is not empty, pop next displayable
    if (empty() == false)
      display.setCurrent((Displayable) pop());
    else
      // On error show an alert
      // Once acknowledged, set 'mainDisplayable' as active
      display.setCurrent(alStackError, mainDisplayable);
  }
}

/*--------------------------------------------------
* Class TimerCanvas
*
* Animate a sequence of images to simulate
* a moving timer
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.com
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/

class TimerCanvas extends Canvas implements Runnable, CommandListener {
  private AnimatedTimer midlet; // Main midlet

  private Command cmExit; // Exit midlet

  private Command cmOptions; // Display options list

  private Image im = null; // Sequence of images

  private int imageCount = 4; // Four images in the sequence

  private int imageWidth; // Width of one image in the sequence

  private int imageHeight; // Height of one image in the sequence

  private int imageIndex; // Current image in the sequence

  private int translate_x; // Translated x and y

  private int translate_y;

  private int viewport_x; // Location of the viewport

  private int viewport_y;

  private boolean active = false; // Timer active?

  private boolean requestedToStop = false; // Did user request to stop timer

  private int sleepTime = 400; // Current sleep time (milliseconds)

  public TimerCanvas(AnimatedTimer midlet) {
    // Call canvas constructor
    super();

    // Save reference to MIDlet so we can
    // access the display manager class
    this.midlet = midlet;

    // Create commands & listen for events
    cmExit = new Command("Exit", Command.EXIT, 1);
    cmOptions = new Command("Config", Command.SCREEN, 2);
    addCommand(cmExit);
    addCommand(cmOptions);
    setCommandListener(this);
  }

  /*--------------------------------------------------
   * Application manager is about to display canvas
   *-------------------------------------------------*/
  protected void showNotify() {
    if (im == null) {
      try {
        // Read the png from a file and get width and
        // height of one image in the sequence
        im = Image.createImage("/timer.png");
        imageHeight = im.getHeight();
        imageWidth = im.getWidth() / imageCount;

        // Get the coordinates for x/y of viewport
        // Viewport is centered on the display
        viewport_x = (getWidth() / 2) - (imageWidth / 2);
        viewport_y = (getHeight() / 2) - (imageHeight / 2);

        // Set first translated coordinate to match viewport
        translate_x = viewport_x;
        translate_y = viewport_y;
      } catch (Exception e) {
        System.err.println("Unable to read png file.");
      }

      // Begin with the first image in the sequence
      imageIndex = 0;
    }

    // If the user has not requested to stop the timer...
    if (!requestedToStop)
      active = true;

    new Thread(this).start();
  }

  /*--------------------------------------------------
   * Application manager is no longer displaying canvas
   *-------------------------------------------------*/
  protected void hideNotify() {
    active = false;
  }

  /*--------------------------------------------------
   * Draw next timer in sequence
   *-------------------------------------------------*/
  protected void paint(Graphics g) {
    if (im != null) {
      // Due to a bug in MIDP 1.0.3 we need to
      // force a clear of the display
      g.setColor(255, 255, 255); // White pen
      g.fillRect(0, 0, getWidth(), getHeight());
      g.setColor(0, 0, 0); // Black pen

      // Viewport at center of display
      g.setClip(viewport_x, viewport_y, imageWidth, imageHeight);

      // Draw image at translated coordinates
      g.drawImage(im, translate_x, translate_y, Graphics.TOP
          | Graphics.LEFT);
    }
  }

  /*--------------------------------------------------
   * Loop forever, translating image coordinates
   *-------------------------------------------------*/
  public void run() {
    try {
      while (active) {
        Thread.sleep(sleepTime);
        repaint();

        // Reached the last image in sequence
        if (imageIndex == imageCount - 1) {
          // Reset translated coordinates
          translate_x = viewport_x;
          translate_y = viewport_y;
        } else {
          // Translate coordinate system to the left
          translate_x -= imageWidth;
        }

        // Which image in the sequence is next
        imageIndex = (imageIndex + 1) % imageCount;
      }
    } catch (InterruptedException e) {
    }
  }

  /*--------------------------------------------------
   * Called from the "Config" options menu
   *-------------------------------------------------*/
  public void startTimer() {
    requestedToStop = false;
    active = true;
    repaint();
  }

  /*--------------------------------------------------
   * Called from the "Config" options menu
   *-------------------------------------------------*/
  public void stopTimer() {
    requestedToStop = true;
    active = false;
    repaint();
  }

  /*--------------------------------------------------
   * Called from form/gauge to adjust sleep
   *-------------------------------------------------*/
  public void setSleep(int sleepTime) {
    this.sleepTime = sleepTime;
  }

  /*--------------------------------------------------
   * Called from form/gauge to adjust sleep
   *-------------------------------------------------*/
  public int getSleep() {
    return sleepTime;
  }

  /*--------------------------------------------------
   * Command event handling
   *-------------------------------------------------*/
  public void commandAction(Command c, Displayable s) {
    if (c == cmOptions) {
      // Push current displayable and show the options list
      midlet.displayMgr.pushDisplayable(midlet.lsOptions);
    } else if (c == cmExit) {
      midlet.exitMIDlet();
    }
  }
}

/*--------------------------------------------------
* Class SleepForm
*
* Form with gauge to adjust sleep interval of timer
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.com
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/

class SleepForm extends Form implements CommandListener {
  private AnimatedTimer midlet; // Main midlet

  private Command cmBack, // Back to options list
      cmHome, // Go to main displayable (canvas)
      cmSave; // Save new sleep time

  private Gauge gaSleep; // Gauge to adjust sleep

  public SleepForm(String title, AnimatedTimer midlet) {
    // Call the form constructor
    super(title);

    // Save reference to MIDlet so we can
    // access the display manager class
    this.midlet = midlet;

    // Commands
    cmSave = new Command("Save", Command.SCREEN, 1);
    cmBack = new Command("Back", Command.BACK, 2);
    cmHome = new Command("Home", Command.SCREEN, 2);

    // Gauge to adjust the length of timer sleep
    gaSleep = new Gauge("Timer Sleep", true, 100, 1000);

    // Set to current sleep. Gauge holds values 0 to 100,
    // divide the current sleep (milliseconds) by 10
    gaSleep.setValue(midlet.cvTimer.getSleep() / 10);

    // Add to form and listen for events
    append(gaSleep);
    addCommand(cmSave);
    addCommand(cmBack);
    addCommand(cmHome);
    setCommandListener(this);
  }

  /*--------------------------------------------------
   * Command event handling
   *-------------------------------------------------*/
  public void commandAction(Command c, Displayable s) {
    if (c == cmSave) {
      // Gauge returns a value between 0 and 100
      // We want milliseconds, so multiply by 10
      midlet.cvTimer.setSleep(gaSleep.getValue() * 10);

      // Return to main midlet
      midlet.displayMgr.home();

    } else if (c == cmBack) {
      // Pop the last displayable off the stack
      midlet.displayMgr.popDisplayable();
    } else if (c == cmHome) {
      // Return to main midlet
      midlet.displayMgr.home();
    }
  }
}

/*--------------------------------------------------
* Class OptionsList
*
* List to provide options for configuring of timer
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.com
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/

class OptionsList extends List implements CommandListener {
  private AnimatedTimer midlet; // Main midlet

  private Command cmBack;

  public OptionsList(String title, int listType, AnimatedTimer midlet) {
    // Call list constructor
    super(title, listType);

    // Save reference to MIDlet so we can
    // access the display manager class
    this.midlet = midlet;

    // Create the list entries
    append("Sleep interval", null);
    append("Start", null);
    append("Stop", null);

    // Create command and listen for events
    cmBack = new Command("Back", Command.BACK, 1);
    addCommand(cmBack);
    setCommandListener(this);
  }

  /*--------------------------------------------------
   * Command event handling
   *-------------------------------------------------*/
  public void commandAction(Command c, Displayable s) {
    // Event generated by the implicit list
    if (c == List.SELECT_COMMAND) {
      switch (getSelectedIndex()) {
      case 0:
        // Push current displayable and show the form
        // to adjust the timer sleep
        midlet.displayMgr.pushDisplayable(midlet.fmSleep);
        break;

      case 1:
        // Start timer and return to previous displayable
        midlet.cvTimer.startTimer();
        midlet.displayMgr.popDisplayable();
        break;

      case 2:
        // Stop timer and return to previous displayable
        midlet.cvTimer.stopTimer();
        midlet.displayMgr.popDisplayable();
        break;
      }
    } else if (c == cmBack) {
      // Return to previous displayable
      midlet.displayMgr.popDisplayable();
    }
  }
}