July 28, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

MIDP Programming with J2ME

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

Low-Level API

In contrast to the high-level API, the low-level API allows full control of the MID display at pixel level. For this purpose, the lcdui package contains a special kind of screen called Canvas. The Canvas itself does not provide any drawing methods, but it does provide a paint() callback method similar to the paint() method in AWT components. Whenever the program manager determines that it is necessary to draw the content of the screen, the paint() callback method of Canvas is called. The only parameter of the paint() method is a Graphics object. In contrast to the lcdui high-level classes, there are many parallels to AWT in the low-level API.

The Graphics object provides all the methods required for actually drawing the content of the screen, such as drawLine() for drawing lines, fillRect() for drawing a filled rectangular area or drawstring() for drawing text strings.

In contrast to AWT, lcdui does not let you mix high-level and low-level graphics. It is not possible to display high-level and low-level components on the screen simultaneously.

The program manager knows that it must call the paint() method of Canvas when the instance of Canvas is shown on the screen. However, a repaint can also be triggered by the application at any time. By calling the repaint() method of Canvas, the system is notified that a repaint is necessary, and it will call the paint() method. The call of the paint() method is not performed immediately; it may be delayed until the control flow returns from the current event handling method. The system may also collect several repaint requests before paint() is actually called. This delay normally is not a problem, but when you're doing animation, the safest way to trigger repaints is to use Display.callSerially() or to request the repaint from a separate Thread or TimerTask. Alternatively, the application can force an immediate repaint by calling serviceRepaints(). (For more information, see the section "Animation" at the end of this chapter.)

The Canvas class also provides some input callback methods that are called when the user presses or releases a key or touches the screen with the stylus (if one is supported by the device).

Basic Drawing

Before we go into the details of user input or animation, we will start with a small drawing example showing the concrete usage of the Canvas and Graphics classes.

The example clears the screen by setting the color to white and filling a rectangle the size of the screen, determined by calling getWidth() and getHeight(). Then it draws a line from coordinates (0,0) to (100,200). Finally, it draws a rectangle starting at (20,30), 30 pixels wide and 20 pixels high:

import javax.microedition.lcdui.*;

class DrawingDemoCanvas extends Canvas {

  public void paint (Graphics g) {
    g.setGrayScale (255);
    g.fillRect (0, 0, getWidth (), getHeight ());
    
    g.setGrayScale (0);
    g.drawLine (0, 0, 100, 200);
    g.fillRect (20, 30, 30, 20);
  }
}

As you can see in the example code, you create a custom class DrawingDemoCanvas in order to fill the paint() method. Actually, it is not possible to draw custom graphics without creating a new class and implementing the paint() method.

In order to really see your Canvas implementation running, you still need a corresponding MIDlet. Here's the missing code:

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

public class DrawingDemo extends MIDlet {

  public void startApp () {
    Display.getDisplay (this).setCurrent (new DrawingDemoCanvas ()); 
  }

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

Now you can start your DrawingDemo MIDlet. Depending on the screen size of the device, it will create output similar to Figure 3.9. In most subsequent examples, you will omit the MIDlet since it is basically the same as this one, except that the name of your Canvas class will be different.

Figure 3.9 Output of the DrawingDemo MIDlet.

In the example, the screen is cleared before drawing because the system relies on the paint() method to fill every pixel of the draw region with a valid value. You don't erase the previous content of the screen automatically because doing so may cause flickering of animations. The application cannot make any assumptions about the content of the Screen before paint() is called. The screen may be filled with the content drawn at the last call of paint(), but it may also be filled with an alert box remaining from an incoming phone call, for example.

Drawing Style and Color

In the DrawingDemoCanvas implementation, you can find two calls to setGrayScale(). The setGrayScale() method sets the gray scale value for the following drawing operations. Valid grayscale values range from 0 to 255, where 0 means black and 255 means white. Not all possible values may actually render to different gray values on the screen. If the device provides fewer than 256 shades of gray, the best fitting value supported by the device is chosen. In the example, the value is first set to white, and the screen is cleared by the following call to drawRect(). Then, the color is set to black for the subsequent drawing operations.

The setGrayScale() method is not the only way to influence the color of subsequent drawing. MIDP also provides a setColor() method. The setColor() method has three parameters holding the red, green, and blue components of the desired color. Again, the values range from 0 to 255, where 255 means brightest and 0 means darkest. If all three parameters are set to the same value, the call is equivalent to a corresponding call of setGrayScale(). If the device is not able to display the desired color, it chooses the best fitting color or grayscale supported by the device automatically. Some examples are listed in Table 3.7.

Table 3.7 Example Color Parameter Settings

Parameter Settings

Resulting Color

setColor (255, 0, 0)

Red

setColor (0, 255, 0)

Green

setColor (0, 0, 255)

Blue

setColor (128, 0, 0)

Dark red

setColor (255, 255, 0)

Yellow

setColor (0, 0, 0)

Black

setColor (255, 255, 255)

White

setColor (128, 128, 128)

50% gray


The only other method that influences the current style of drawing is the setStrokeStyle() method. The setStrokeStyle() command sets the drawing style of lines to dotted or solid. You determine the style by setting the parameter to one of the constants DOTTED or SOLID, defined in the Graphics class.

When the paint() method is entered, the initial drawing color is always set to black and the line style is SOLID.

Simple Drawing Methods

In the example, you have already seen fillRect() and drawLine(). Table 3.8 shows all drawing primitives contained in the Graphics class. All operations where the method names begin with draw, except drawstring() and drawImage(), are influenced by the current color and line style. They draw the outline of a figure, whereas the fill methods fill the corresponding area with the current color and do not depend on the line style.

Table 3.8 Drawing Methods of the Graphics Class

Method

Purpose

drawImage (Image image,

Draws an Image. Explained in detail in the int x, int y, int align) "Images" section.

drawString (String text,

Draws a text string at the given position in the int x, int y, int align) current color; see "Text and Fonts."

drawRect (int x, int y,

Draws an empty rectangle with the upper-left int w, int h) corner at the given (x,y)coordinate, with the given width and a height. The next section explains why the rectangle is one pixel larger than you might expect.

drawRoundRect (int x, int y,

Like drawRect(), except that an additional radius int w, int h, int r) is given for rounded corners of the rectangle.

drawLine (int x0, int y0,

Draws a line from (x0,y0) to (x1,y1). int x1, int y1)

drawArc (int x, int y, Draws the outline of a circular or elliptical arc int w, int h,

covering the specified rectangle, using the current int startAng, int arcArc) color and stroke style. The resulting arc begins at startAng and extends for arcAng degrees. Angles are interpreted such that 0 degrees is at the 3 o'clock position. A positive value indicates a counter-clockwise rotation while a negative value indicates a clockwise rotation.

fillRect (int x, int y,

Similar to drawRect(), but fills the given area int w, int h) with the current color.

fillRoundRect (int x, int y,

Related to fillRect() as drawRoundRect() is int w, int h, related to drawRect(). int startAng, int endAng);

fillArc (int x, int y,

Like drawArc(), but fills the corresponding region. int w, int h, int startAng, int endAng);


Coordinate System and Clipping

In the drawing example, we already have used screen coordinates without explaining what they actually mean. You might know that the device display consists of little picture elements (pixels). Each of these pixels is addressed by its position on the screen, measured from the upper-left corner of the device, which is the origin of the coordinate system. Figure 3.10 shows the lcdui coordinate system.

Actually, in Java the coordinates do not address the pixel itself, but the space between two pixels, where the "drawing pen" hangs to the lower right. For drawing lines, this does not make any difference, but for rectangles and filled rectangles this results in a difference of one pixel in width and height: In contrast to filled rectangles, rectangles become one pixel wider and higher than you might expect. While this may be confusing at first glance, it respects the mathematical notation that lines are infinitely thin and avoids problems when extending the coordinate system to real distance measures, as in the J2SE class Graphics2D.

Figure 3.10 The lcdui coordinate system.

In all drawing methods, the first coordinate (x) denotes the horizontal distance from the origin and the second coordinate (y) denotes the vertical distance. Positive coordinates mean a movement down and to the right. Many drawing methods require additional width and height parameters. An exception is the drawLine() method, which requires the absolute coordinates of the destination point.

The origin of the coordinate system can be changed using the translate() method. The given coordinates are added to all subsequent drawing operations automatically. This may make sense if addressing coordinates relative to the middle of the display is more convenient for some applications, as shown in the section "Scaling and Fitting," later in the chapter.

The actual size of the accessible display area can be queried using the getWidth() and getHeight() methods, as performed in the first example that cleared the screen before drawing. The region of the screen where drawing takes effect can be further limited to a rectangular area by the clipRect() method. Drawing outside the clip area will have no effect.

The following example demonstrates the effects of the clipRect() method. First, a dotted line is drawn diagonally over the display. Then a clipping region is set. Finally, the same line as before is drawn using the SOLID style:

import javax.microedition.lcdui.*;

class ClipDemoCanvas extends Canvas {

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

    int m = Math.min (getWidth (), getHeight ());    
    g.setGrayScale (0);

    g.setStrokeStyle (Graphics.DOTTED);
    g.drawLine (0, 0, m, m);

    g.setClip (m / 4, m / 4, m / 2, m / 2);

    g.setStrokeStyle (Graphics.SOLID);
    g.drawLine (0, 0, m, m);
  }
}

Figure 3.11 shows the resulting image. Although both lines have identical start and end points, only the part covered by the clipping area is replaced by a solid line.

Figure 3.11 Output of the clipRect() example: Only the part covered by the clipping area is redrawn solid, although the line coordinates are identical.

When the paint() method is called from the system, a clip area may already be set. This may be the case if the application just requested repainting of a limited area using the parameterized repaint call, or if the device just invalidated a limited area of the display, for example if a pop-up dialog indicating an incoming call was displayed but did not cover the whole display area.

Actually, clipRect() does not set a new clipping area, but instead shrinks the current clip area to the intersection with the given rectangle. In order to enlarge the clip area, use the setClip() method.

The current clip area can be queried using the getClipX(), getClipY(), getClipWidth(), and getClipHeight() methods. When drawing is computationally expensive, this information can be taken into account in order to redraw only the areas of the screen that need an update.

Text and Fonts

For drawing text, lcdui provides the method drawstring(). In addition to the basic drawstring() method, several variants let you draw partial strings or single characters. (Details about the additional methods can be found in the lcdui API documentation.) The simple drawstring() method takes four parameters: The character string to be displayed, the x and y coordinates, and an integer determining the horizontal and vertical alignment of the text. The alignment parameter lets you position the text relative to any of the four corners of its invisible surrounding box. Additionally, the text can be aligned to the text baseline and the horizontal center. The sum or logical or (|) of a constant for horizontal alignment (LEFT, RIGHT, and HCENTER) and constants for vertical alignment (TOP, BOTTOM, and BASELINE) determine the actual alignment. Figure 3.12 shows the anchor points for the valid constant combinations.

Figure 3.12 Valid combinations of the alignment constants and the corresponding anchor points.

The following example illustrates the usage of the drawstring() method. By choosing the anchor point correspondingly, the text is displayed relative to the upper-left and lower-right corner of the screen without overlapping the screen border:

import javax.microedition.lcdui.*;

class TextDemoCanvas extends Canvas {

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

    g.setGrayScale (0);
    g.drawString ("Top/Left", 0, 0, Graphics.TOP | Graphics.LEFT);
    g.drawString ("Baseline/Center", getWidth () / 2, getHeight () / 2, 
           Graphics.HCENTER | Graphics.BASELINE);
    g.drawString ("Bottom/Right", getWidth (), getHeight (), 
           Graphics.BOTTOM | Graphics.RIGHT);
  }
}

Figure 3.13 shows the output of the TextDemo example.

Figure 3.13 Output of the TextDemo example.

In addition to the current drawing color, the result of the drawstring() method is influenced by the current font. MIDP provides support for three different fonts in three different sizes and with the three different attributes: bold, italic, and underlined.

A font is not selected directly, but the setFont() method takes a separate Font object, describing the desired font, as a parameter. The explicit Font class provides additional information about the font, such as its width and height in pixels, baseline position, ascent and descent, and so on. Figure 3.14 illustrates the meaning of the corresponding values. This information is important for operations such as drawing boxes around text strings. In addition, word-wrapping algorithms rely on the actual pixel width of character strings when rendered to the screen.

Figure 3.14 Font properties and the corresponding query methods.

A Font object is created by calling the static method createFont() of the class Font in the lcdui package. The createFont() method takes three parameters: the font type, style, and size of the font. Similar to the text alignment, there are predefined constants for setting the corresponding value; these constants are listed in Table 3.9.

Table 3.9 createFont() Property Constants

Property

Constants

Size

SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE

Style

STYLE_PLAIN, STYLE_ITALICS, STYLE_BOLD, STYLE_UNDERLINED

Face

FACE_SYSTEM, FACE_MONOSPACE, FACE_PROPORTIONAL


The style constants can be combined—for example, STYLE_ITALICS | STYLE_BOLD will result in a bold italics font style.

The following example shows a list of all fonts available, as far as the list fits on the screen of the device:

import javax.microedition.lcdui.*;

class FontDemoCanvas extends Canvas {

  static final int [] styles = {Font.STYLE_PLAIN, 
                 Font.STYLE_BOLD, 
                 Font.STYLE_ITALIC};
  static final int [] sizes = {Font.SIZE_SMALL, 
                 Font.SIZE_MEDIUM, 
                 Font.SIZE_LARGE};
  static final int [] faces = {Font.FACE_SYSTEM, 
                 Font.FACE_MONOSPACE, 
                 Font.FACE_PROPORTIONAL};

  public void paint (Graphics g) {
    Font font = null;
    int y = 0;
    g.setGrayScale (255);
    g.fillRect (0, 0, getWidth (), getHeight ());
    g.setGrayScale (0);

    for (int size = 0; size < sizes.length; size++) {
      for (int face = 0; face < faces.length; face++) {
        int x = 0;
        for (int style = 0; style < styles.length; style++) {
          font = Font.getFont 
               (faces [face], styles [style], sizes [size]);
          g.setFont (font);
          g.drawString 
            ("Test", x+1, y+1, Graphics.TOP | Graphics.LEFT);
          g.drawRect 
            (x, y, font.stringWidth ("Test")+1, 
             font.getHeight () + 1);
          x += font.stringWidth ("Test")+1;
        }
        y += font.getHeight () + 1;
      }
    }
  }
}

Figure 3.15 shows the output of the FontDemo example.

Figure 3.15 Output of the FontDemo example.





Page 5 of 8



Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel