ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Learning Wireless Java

MIDP GUI Programming, Part 3

Related Reading

Wireless Java
Help for New J2ME Developers
By Qusay Mahmoud

by Qusay Mahmoud

Creating Low-Level GUI Components

This is the third and final in a series of articles from O'Reilly's Learning Wireless Java, by Qusay Mahmoud. This section from Chapter 5 focuses on the low-level MIDP GUI APIs and components.

In the high-level API, you have no control of what is displayed on the screen and very little freedom to "play" with the components programmatically. The implementation is responsible for selecting the best approach for the device. Some applications, however, such as games, may need more control over what is drawn on the screen. The MIDP javax.microedition.lcdui package also provides a low-level API for handling such cases.

In order to directly draw lines, text, and shapes on the screen, you must use the Canvas class. The Canvas class provides a blank screen on which a MIDlet can draw. For example, let's draw the string "HelloWorld" on the screen. There's a simple way to do this: subclass the Canvas class, which is an abstract class that extends Displayable, and override the paint( ) method. The resulting class, MyCanvas, is shown in Example 5-1.

The implementation of the paint( ) method uses the drawing capabilities of the javax.microedition.lcdui.Graphics class. In the paint( ) method, the drawing color is set to red, then a rectangle is drawn in the current color. The methods getWidth( ) and getHeight( ) return the width and height of the Canvas, respectively. The next call to setColor( ) sets the drawing color to white; then the string "Hello World!" is drawn in the top left corner of the screen.


Example 5-1: Subclassing Canvas

import javax.microedition.lcdui.*;
 
public class MyCanvas extends Canvas {
   public void paint(Graphics g) {
      g.setColor(255, 0, 0);
      g.fillRect(0, 0, getWidth(), getHeight(  ));
      g.setColor(255, 255, 255);
      g.drawString("Hello World!", 0, 0, g.TOP | g.LEFT);
   }
}

Now, in order to view the MyCanvas, it must be instantiated and displayed. Since Canvas is a subclass of Displayable, it can be displayed the same way any other screen using the setCurrent( ) method. Example 5-2 shows the resulting MIDlet.


Example 5-2: Instantiating and displaying MyCanvas

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
 
public class MyMidlet extends MIDlet {
   public MyMidlet(  ) { // constructor
   }
 
   public void startApp(  ) {
      Canvas canvas = new MyCanvas(  );
      Display display = Display.getDisplay(this);
      display.setCurrent(canvas);
   }
 
   public void pauseApp(  ) {
   }
 
   public void destroyApp(boolean unconditional) {
   }
}

If you run this in the Wireless Toolkit emulator, you will see something similar to Figure 1. Note from Example 5-1 that the colors are set to red and white, but since a grayscale display is being used, the colors are mapped to appropriate shades of black and white. Try switching displays to see which devices give a better feel for the colors.

Screen shot.
Figure 1. Drawing "Hello World!" on a Canvas.

Drawing Graphics

The (0,0) coordinate represents the upper left corner of the display. The numeric value of the x-coordinate increases from left to right, and the numeric value of the y-coordinate increases from top to bottom. Applications should always check the dimensions of the drawing area by using the following methods of the Canvas class:

public int getHeight(  );
public int getWidth(  );

These two methods return the height and width of the displayable area in pixels, respectively.

Previously in this series:

MIDP GUI Programming, Part 2 -- This is the second in a series of excerpts from Chapter 5 of Learning Wireless Java. It discusses how the various classes in the high-level MIDP API can be used to create GUI components.

MIDP GUI Programming, Part 1 -- This is the first in a series of excerpts from Chapter 5 of Learning Wireless Java. Why not use AWT? Well, MIDP contains its own abbreviated GUI, which is much different from AWT. This chapter excerpt introduces you to the MIDP GUI APIs.

The drawing model used is called pixel replacement. It works by replacing the destination pixel value with the current pixel value specified in the graphics objects being used for rendering. A 24-bit color model is provided with 8 bits each for Red, Green, and Blue (RGB). However, since not all devices support color, colors requested by applications will be mapped into colors available on the devices. A well-written application, however, may check if a device supports color. This can be done using the isColor() and numColors( ) methods of the Display class, which we covered earlier in the chapter.

The Graphics class provides the setColor() and getColor( ) methods for setting and getting the color. Unlike the AWT/Swing, however, there is no setBackground( ) and setForeground(), so you need to explicitly call fillRect( ), as shown in Example 5-1. Most of the other methods in the Graphics class are self-explanatory and similar to methods in the AWT version of this class. However, let's go over a few of them here to see how they work in the J2ME environment.

Double Buffering

The double buffering technique is often used to perform smooth effect animation. In this technique, you do not draw to the display, but instead to a copy of the display (an off-screen buffer) that is maintained in memory. When you are done drawing to the buffer, you then copy the contents of the buffer to the display. The rationale here is that copying the contents of memory to the display is faster than drawing by using primitives.

To implement double buffering, first create a mutable image with the size of the screen:

int width = getWidth(  );
int height = getHeight(  );
Image buffer = Image.createImage(width, height);

Next, obtain a graphics context for the buffer:

Graphics gc = buffer.getGraphics(  );

Now, you can draw to the buffer:

// animate
// ..
gc.drawRect(20, 20, 25, 30);

When you need to copy the buffer to the screen, you can override the paint( ) method to draw the buffer to the device display:

public void paint(Graphics g) {
   g.drawImage(buffer, 0, 0, 0);
}

Note that some MIDP implementations are already double-buffered, and therefore this work may not be necessary. To check if the graphics are double-buffered by an implementation, use the Canvas. isDoubleBuffered( ) method.

Threading Issues

The MIDP GUI APIs are thread-safe. In other words, the methods can be called at any time from any thread. The only exception is the serviceRepaints( ) method of the Canvas class, which immediately calls the paint( ) method to force immediate repainting of the display. This means that if paint( ) tries to synchronize on any object that is already locked by the application when serviceRepaints( ) is called, the application will deadlock. To avoid deadlocks, do not lock an object that will be used by the paint() method if serviceRepaints( ) is involved.

In addition, you can use the callSerially( ) method of the Display class to execute code after all pending repaints are served, as shown in the following segment of code:

class TestCanvas extends Canvas implements Runnable {
   void doSomething(  ) {
      // code fragment 1
      callSerially(this);
   }
 
   public void run(  ) {
      // code fragment 2
   }
}

Here, the object's run( ) method will be called after the initial call.

Fonts

Fonts cannot be created by applications. Instead, an application requests a font based on attributes (i.e., size, face, style) and the underlying implementation will attempt to return a font that closely resembles the requested font. The Font class represents various fonts and metrics. There are three font attributes defined in the Font class, and each may have different values, as follows:

Face
MONOSPACE, PROPORTIONAL, SYSTEM
Size
SMALL, MEDIUM, LARGE
Style
BOLD, ITALIC, PLAIN, UNDERLINED

For example, to specify a medium size font, use Font.SIZE_MEDIUM, and to specify an italic style, use Font.STYLE_ITALIC, and so on. Values for the style attributes may be combined using the OR (|) operator; values for the other attributes may not be combined. For example, the value of this style attribute specifies a plain, underlined font:

STYLE_PLAIN | STYLE_UNDERLINED

However, the following is illegal:

SIZE_SMALL | SIZE_MEDIUM

This is also illegal:

FACE_SYSTEM | FACE_MONOSPACE

Each font in the system is actually implemented individually, so in order to obtain an object representing a font, use the getFont( ) method. This method takes three arguments for the font face, size, and style, respectively. For example, the following snippet of code obtains a Font object with the specified face, style, and size attributes:

Font font = Font.getFont(FACE_SYSTEM, 
  STYLE_PLAIN, SIZE_MEDIUM);

If a matching font does not exist, the implementation will attempt to provide the closest match, which is always a valid Font object.

Once a font is obtained, you can use methods from the Font class to retrieve information about that font. For example, you can use the methods getFace(), getSize( ), and getStyle( ) to retrieve information about the face, size, and style of the font, respectively.

Let's look at an example. The code in Example 5-3 subclasses the Canvas class. In this example, the drawing color is set to white, a rectangle is drawn in the current color, then the drawing color is set to black. The rest of the code draws the system fonts on the device screen, as shown in Figure 2.

Screen shot.
Figure 2. Drawing system fonts on the device screen.

Example 5-3: Using fonts

import javax.microedition.lcdui.*;
 
public class FontCanvas extends Canvas {
   public void paint(Graphics g) {
      g.setColor(0xffffff);
      g.fillRect(0, 0, getWidth(), getHeight(  ));
      g.setColor(0x000000);
 
      g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,
          Font.SIZE_LARGE));
      g.drawString("System Font", 0, 0, g.LEFT | g.TOP);
      g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,
          Font.SIZE_MEDIUM));
      g.drawString("Medium Size", 0, 15, g.LEFT | g.TOP);
      g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,
          Font.SIZE_MEDIUM));
      g.drawString("Bold Style", 0, 30, g.LEFT | g.TOP);
      g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC,
          Font.SIZE_MEDIUM));
      g.drawString("Italic Style", 0, 45, g.LEFT | g.TOP);
      g.setFont(Font.getFont(Font.FACE_SYSTEM,
          Font.STYLE_UNDERLINED, Font.SIZE_MEDIUM));
      g.drawString("Underlined Style", 0, 60, g.LEFT | g.TOP);
   }
}

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
 
public class FontMidlet extends MIDlet {
   public FontMidlet(  ) { // constructor
   }
 
   public void startApp(  ) {
      Canvas canvas = new FontCanvas(  );
      Display display = Display.getDisplay(this);
      display.setCurrent(canvas);
   }
 
   public void pauseApp(  ) {
   }
 
   public void destroyApp(boolean unconditional) {
   }
}

Guidelines for GUI Programming for MIDP Devices

As we close this chapter, keep in mind some important guidelines when designing MIDlets with graphical API functionality:


Previously in this series:

MIDP GUI Programming, Part 2 -- This is the second in a series of excerpts from Chapter 5 of Learning Wireless Java. It discusses how the various classes in the high-level MIDP API can be used to create GUI components.

MIDP GUI Programming, Part 1 -- This is the first in a series of excerpts from Chapter 5 of Learning Wireless Java. Why not use AWT? Well, MIDP contains its own abbreviated GUI, which is much different from AWT. This chapter excerpt introduces you to the MIDP GUI APIs.

View catalog information for Learning Wireless Java


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.