November 28, 2014
Hot Topics:

MIDP Programming with J2ME

  • December 26, 2002
  • By Sams Publishing
  • Send Email »
  • More Articles »

Animation

With animation, there are normally two main problems: Display flickering and synchronization of painting with calculation of new frames. We will first address how to get the actual painting and application logic in sync, and then solve possible flickering.

Synchronization of Frame Calculation and Drawing

When you perform animations, you can first calculate the display content and then call repaint() in order to paint the new frame. But how do you know that the call to paint() has finished? One possibility would be to call serviceRepaints(), which blocks until all pending display updates are finished. The problem with serviceRepaints() is that paint() may be called from another thread. If the thread calling serviceRepaints() holds any locks that are required in paint(), a deadlock may occur. Also, calling serviceRepaints() makes sense only from a thread other than the event handling thread. Otherwise, key events may be blocked until the animation is over. An alternative to serviceRepaints() is calling callSerially() at the end of the paint() method. The callSerially() method lets you put Runnable objects in the event queue. The run() method of the Runnable object is then executed serially like any other event handling method. In the run() method, the next frame can be set up, and a new repaint can be requested there.

To demonstrate this execution model, you will build a simple stopwatch that counts down a given number of seconds by showing a corresponding pie slice using the fillArc() method, as shown in Figure 3.21.

Figure 3.21 A very simple stopwatch.

The Canvas implementation stores the current slice in degree, the start time, the total amount of seconds and the MIDlet display in local variables. In order to make use of callSerially(), your Canvas implements the Runnable interface:

class StopWatchCanvas extends Canvas implements Runnable {
  int degree = 360;
  long startTime;
  int seconds;
  Display display;

When the StopWatchCanvas is created, you store the given display and seconds. Then, the current time is determined and stored, too:

StopWatchCanvas (Display display, int seconds) {
  this.display = display;
  this.seconds = seconds;
  startTime = System.currentTimeMillis ();
}

In the paint() method, you clear the display. If you need to draw more than 0 degrees, you fill a corresponding arc with red color and request recalculation of the pie slice using callSerially(). Finally, you draw the outline of the stopwatch by setting the color to black and calling drawArc():

public void paint (Graphics g) {
  g.setGrayScale (255);
  g.fillRect (0, 0, getWidth (), getHeight ());

  if (degree > 0) {
    g.setColor (255, 0, 0);
    g.fillArc (0,0, getWidth (), getHeight (), 90, degree);
    display.callSerially (this);
  }
  g.setGrayScale (0);
  g.drawArc (0, 0, getWidth ()-1, getHeight ()-1, 0, 360);
}

This method is invoked by the event handling thread as a result of the previous display.callSerially(this) statement. In this case, it just calculates a new pie slice and requests a repaint():

  public void run () {
    int permille = (int) ((System.currentTimeMillis () 
              - startTime) / seconds); 
    degree = 360 - (permille * 360) / 1000; 
    repaint ();
  }
}

As always, you need a MIDlet to actually display your StopWatchCanvas implementation. The following code creates a stopwatch set to 10 seconds when the application is started:

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class StopWatch extends MIDlet {

  public void startApp () {
    Display display = Display.getDisplay (this);
    display.setCurrent (new StopWatchCanvas (display, 10)); 
  }

  public void pauseApp () {
  }
  
  public void destroyApp (boolean forced) {
  }
}

Avoiding Flickering

On some devices, the stopwatch implementation will flicker. This is due to the fact that the display is cleared completely before a new stopwatch is drawn. However, on some other devices, the stopwatch will not flicker because those devices provide automated double buffering. Before the screen is updated, all drawing methods are performed in a hidden buffer area. Then, when the paint() method is finished, the complete display is updated from the offscreen buffer at once. The method isDoubleBuffered() in the Canvas class is able to determine whether the device screen is double buffered.

In order to avoid flickering of your animation in all cases, you can add your own offscreen image, which is allocated only if the system does not provide double buffering:

Image offscreen = isDoubleBuffered () ? null : 
           Image.createImage (getWidth (), getHeight ());

In the paint() method, you just check if the offscreen image is not null, and if so, you delegate all drawing to your offscreen buffer. The offscreen buffer is then drawn immediately at the end of the paint() method, without first clearing the screen. Clearing the screen is not necessary in that case since the offscreen buffer was cleared before drawing and it fills every pixel of the display:

public void paint (Graphics g) {
  Graphics g2 = offscreen == null ? g : offscreen.getGraphics ();
    
  g2.setGrayScale (255);
  g2.fillRect (0, 0, getWidth (), getHeight ());

  if (degree > 0) {
    g2.setColor (255, 0, 0);
    g2.fillArc (0,0, getWidth (), getHeight (), 90, degree);
    
    display.callSerially (this);
  }

  g2.setGrayScale (0);
  g2.drawArc (0, 0, getWidth ()-1, getHeight ()-1, 0, 360);

  if (offscreen != null) 
    g.drawImage (offscreen, 0, 0, Graphics.TOP | Graphics.RIGHT); 
}

Listing 3.3 gives the complete source code for the buffered stopwatch.

Listing 3.3 BufferedStopWatch.java —The Complete Source Code of the Buffered Stopwatch

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

class BufferedStopWatchCanvas extends Canvas implements Runnable {
  int degree = 360;
  long startTime;
  int seconds;
  Display display;
  Image offscreen;

  BufferedStopWatchCanvas (Display display, int seconds) {
    this.display = display;
    this.seconds = seconds;

    if (!isDoubleBuffered () && false)
      offscreen = Image.createImage (getWidth (), getHeight ());

    startTime = System.currentTimeMillis ();    
  }

  public void paint (Graphics g) {
    
    Graphics g2 = offscreen == null 
      ? g
      : offscreen.getGraphics ();
      
    g2.setGrayScale (255);
    g2.fillRect (0, 0, getWidth (), getHeight ());

    if (degree > 0) {
      g2.setColor (255, 0, 0);
      g2.fillArc (0,0, getWidth (), getHeight (), 90, degree);
      
      display.callSerially (this);
    }

    g2.setGrayScale (0);
    g2.drawArc (0, 0, getWidth ()-1, getHeight ()-1, 0, 360);

    if (offscreen != null) 
      g.drawImage (offscreen, 0, 0, Graphics.TOP | Graphics.RIGHT); 
  }

  public void run () {
    int permille = (int) ((System.currentTimeMillis () 
                - startTime) / seconds); 
    degree = 360 - (permille * 360) / 1000; 
    repaint ();
  }
}


public class BufferedStopWatch extends MIDlet {

  public void startApp () {
    Display display = Display.getDisplay (this);
    display.setCurrent (new BufferedStopWatchCanvas (display, 10)); 
  }

  public void pauseApp () {
  }
  
  public void destroyApp (boolean forced) {
  }
}

Summary

In this chapter, you learned the general life cycle of MIDP applications. You know how to build a user interface using the high-level lcdui widgets, and how to interact using the listener mechanism. You have learned to perform custom graphics using the low-level API, including device-independent flicker-free animation and coordination of graphics calculation and drawing.

The next chapter gives a corresponding overview of the PDAP life cycle and user interface. The PDAP introduction focuses on the differences between the J2SE AWT classes and the subset included in PDAP, but still gives a basic introduction to AWT programming.

About the Authors

Michael Kroll and Stefan Haustein are the creators of the kAWT-Project, an abstract window toolkit designed to allow graphical J2ME programming on devices using the KVM. Since the first release of KVM, both have been members of the Expert group specifying the J2ME PDA Profile.

Michael Kroll's experience in professional J2ME programming includes an application for viewing biosignals like ECGs and EEGs on a Palm organizer. Michael is working for Visus Technology Transfer in Germany developing a Java based radiology imaging system called JiveX.

Stefan Haustein studied Computer Science at the University of Dortmund, and is working on his Ph.D in the AI-Unit. He wrote his diploma thesis in the Neuros-Project at the "Institut fur Neuroinformatik" at the University of Bochum about graph-based robot navigation.

Source of this material

This is Chapter 3: MIDP Programming from the book J2ME Application Development (ISBN:0-672-323095-9) written by Michael Kroll and Stefan Haustein, published by Sams Publishing.

To access the full Table of Contents for the book


Other Chapters from Sams Publishing:

Web Services and Flows (WSFL)
Overview of JXTA
Introduction to EJBs
Processing Speech with Java
The Java Database Control in BEA Weblogic





Page 8 of 8



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel