October 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Programming MIDlet Graphics using the Canvas Class

  • July 15, 2008
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2588


Preface

This is Part 1 of a two-part lesson in a series of tutorial lessons designed to teach you how to write programs using the Sun Java Wireless Toolkit for CLDC. The first lesson was titled Getting Started with MIDlets and the Sun Java Wireless Toolkit for CLDC. The previous lesson was titled Programming MIDlets for Interactive Behavior (see Resources).

What you will learn

In this lesson, you will learn how to use several methods of the Canvas class (including paint and repaint), along with several methods of the Graphics class to draw text and lines on the cell phone screen. You will learn how to use anchor point coordinates to control the position of text that is drawn on the screen. You will also learn how to handle keyPressed events fired by a Canvas object.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. Partial class hierarchy for MIDP 2.0.
  • Figure 2. Output from MIDlet named Canvas01 in Sun emulator.
  • Figure 3. Sun cell phone emulator output for the MIDlet named Canvas02.

Listings

  • Listing 1. The main class for the MIDlet named Canvas01.
  • Listing 2. Beginning of the member class named MyCanvas.
  • Listing 3. Draw eight lines and eight text strings.
  • Listing 4. Draw two more lines.
  • Listing 5. Beginning of the class for the MIDlet named Canvas02.
  • Listing 6. Remainder of the main class for the MIDlet named Canvas02.
  • Listing 7. Beginning of the member class named MyCanvas.
  • Listing 8. Beginning of the overridden paint method.
  • Listing 9. Remainder of the overridden paint method.
  • Listing 10. Overridden keyPressed method.
  • Listing 11. Source code for the MIDlet named Canvas01.
  • Listing 12. Source code for the MIDlet named Canvas02.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com.

General background information

If you are familiar with graphics programming in J2SE without the benefit of Java 2D and Java 3D, you will probably be reasonably comfortable with the material in this lesson. J2ME graphics is very similar to graphics programming in the early days of Java, but with some interesting new wrinkles.

Partial class hierarchy for MIDP 2.0

A partial class hierarchy for MIDP 2.0 is shown in Figure 1. Figure 1 includes all of the classes that are included in the package named javax.microedition.lcdui. In addition, Figure 1 includes three classes that are in other packages.

Figure 1. Partial class hierarchy for MIDP 2.0.

  • Object
    • Displayable
      • Screen
        • TextBox
        • Alert
        • List
        • Form
      • Canvas (abstract)
        • GameCanvas (abstract)
    • Display
    • Ticker
    • AlertType
    • Image
    • Item
      • Gauge
      • ChoiceGroup
      • CustomItem
      • DateField
      • ImageItem
      • Spacer
      • StringItem
      • TextField
    • Timer
    • TimerTask
    • Command
    • Graphics
    • Font

Classes to be illustrated

I have discussed and illustrated the classes shown in boldface Italics (plus the Choice interface, the CommandListener interface, and the ItemCommandListener interface) in earlier lessons (see Resources). I will discuss and illustrate the following classes in this lesson:

  • Canvas class
  • Graphics class
  • Font class

I will discuss and illustrate the GameCanvas class in a future lesson.  I will leave CustomItem as an exercise for the student.

The Canvas class

Here is part of what Sun has to say about the Canvas class:

"The Canvas class is a base class for writing applications that need to handle low-level events and to issue graphics calls for drawing to the display. Game applications will likely make heavy use of the Canvas class. From an application development perspective, the Canvas class is interchangeable with standard Screen classes, so an application may mix and match Canvas with high-level screens as needed. For example, a List screen may be used to select the track for a racing game, and a Canvas subclass would implement the actual game."

The use of the Canvas class and the GameCanvas class for programming games will be the topic of a future lesson. In this lesson, I will concentrate on using the Canvas class for presenting graphic information on the cell phone screen, and handling key events that are fired by a Canvas object.

Yes, I really did mean key events

Unlike the commands that we studied in an earlier lesson, the Canvas class provides the ability to detect and handle key events in a manner that is very similar, but not identical to the JavaBeans or Delegation event model in J2SE (see Resources).

Pointer events

The Canvas class also provides the ability to detect and handle pointer (mouse like) events on hardware implementations that support them.  However, in these lessons, I am constrained to the use of the Sun cell phone emulator, which does not appear to support pointer events. (If it does support pointer events, I haven't figured out how to make it do so.) Therefore, I will have very little, if anything, to say about pointer events.

Canvas also supports commands

Just like other Displayable objects, Canvas objects also support commands such as EXIT, BACK, OK, etc. (see Programming MIDlets for Interactive Behavior in Resources). Therefore, you can mix command programming and real event-driven programming for Canvas objects.

Canvas is an abstract class

The Canvas class is abstract, so you must extend it in order to use it. The reason for this is that the primary use of the Canvas class is to serve as a surface on which to draw. In order to draw, you must override the paint method of the Canvas class. The only way to override a method is to extend the class to which the method belongs.

Comparison with J2SE

When programming in J2SE (using the JavaBeans source-listener event model), if you want to detect and handle key events on an object that is the source of events, you must:

  • Define a class that implements the KeyListener interface.
  • Instantiate an object of that class.
  • Register the listener object on the source object.

Event handler methods, such as keyPressed are declared in the KeyListener interface and must be defined in your new class. That is not the case with MIDP 2.0. The Canvas class already defines the following empty event-handler methods:

  • keyPressed
  • keyReleased
  • keyRepeated
  • pointerDragged
  • pointerPressed
  • pointerReleased

Handling an event

To handle one of the events in the above list, you must override the corresponding method in your subclass of the Canvas class, writing the desired behavior into your overridden method. If you don't want to handle any of the events in the above list, just forget about the ones that you don't want to handle. Unlike the JavaBeans event model, it is not necessary to define empty methods for the events that you don't want to handle.

More information from Sun

According to Sun,

"Applications receive keystroke events in which the individual keys are named within a space of key codes. Every key for which events are reported to MIDP applications is assigned a key code. The key code values are unique for each hardware key unless two keys are obvious synonyms for each other."

Sun goes on to tell us,

"MIDP defines the following key codes: KEY_NUM0, KEY_NUM1, KEY_NUM2, KEY_NUM3, KEY_NUM4, KEY_NUM5, KEY_NUM6, KEY_NUM7, KEY_NUM8, KEY_NUM9, KEY_STAR, and KEY_POUND. (These key codes correspond to keys on a ITU-T standard telephone keypad.)"

Public static final variables

The Canvas class provides a public static final variable (constant) for each of the key codes listed above.  Finally, Sun tells us,

"Other keys may be present on the keyboard, and they will generally have key codes distinct from those list above. In order to guarantee portability, applications should use only the standard key codes."

Did not adhere to the rule

As you will see later, I did not adhere to this rule in one of the sample MIDlets. I wasn't interested in achieving portability. Rather, I was mainly interested in clarity and I found it easier to be clear using the following key names (which may be peculiar to the Sun emulator) in the MIDlet:

  • LEFT
  • RIGHT
  • UP
  • DOWN
  • SELECT

These names apply to the arrow keys and the large key in the middle of the arrow keys on the Sun cell phone emulator keypad (see Figure 2).; As mentioned above, they may not apply to similar keys in other cell phone emulators or in real cell phones for that matter. (There is a better way to deal with the arrow keys, which you will learn about in a future lesson on the GameCanvas class.)

Normal versus full-screen mode

Here is some of what Sun has to say on this topic:

"A Canvas can be in normal mode or in full-screen mode. In normal mode, space on the display may be occupied by command labels, a title, and a ticker. By setting a Canvas into full-screen mode, the application is requesting that the Canvas occupy as much of the display space as is possible. In full-screen mode, the title and ticker are not displayed even if they are present on the Canvas, and Commands may be presented using some alternative means (such as through a pop-up menu)."

A Canvas object is in normal mode by default. You will see an example of normal and full-screen modes in one of the sample MIDlets in this lesson.

Numerous methods are defined in the Canvas class. I will illustrate the use of many of those methods in the sample MIDlets in this lesson.

The Graphics class

Sun describes this class as follows:

"Provides simple 2D geometric rendering capability."

While true, this statement hardly does justice to the importance of the Graphics class.

No public constructor

The Graphics class doesn't have a public constructor, so you can't directly instantiate an object of the Graphics class. I am aware of at least two ways to get access to an object of the class.

The overridden paint method

The paint method that I mentioned earlier is a callback method. Whenever your Canvas object is visible on the screen and the system needs to display graphics information on that canvas, it will call your overridden paint method. When your overridden paint method is called, it will receive a reference to an object of Graphics class.

You can think of that object as a drawing surface that represents the screen. When you call the various methods of the Graphics class to draw pictures on the Graphics object, those pictures will appear on the screen (assuming that your MIDlet currently has access to the screen).

The createImage and getGraphics methods

As you will see in one the sample MIDlets in this lesson, you can use the createImage and getGraphics methods together to get access to an off-screen drawing area that is represented by a Graphics object. When you use the various methods of the Graphics class to draw on that Graphics object, the results are not immediately visible. Rather, you are simply drawing in memory when you do that and only you know what is happening.

This makes it possible for you to compose a complex drawing in memory and then transfer it to the Canvas object (the screen) very quickly by calling the drawImage method on the Canvas passing the off-screen image's reference as a parameter. This is often a more visually pleasing approach than composing a complex drawing on the screen in full view of the user, particularly if the time required to compose the drawing is significant.

What about circles?
Circles are constructed from 360-degree circular arcs. Note that for the case of the drawArc method, angles are specified in degrees instead of radians.

Drawing primitives

The methods of the Graphics class provide drawing primitives for text, images, lines, rectangles, and arcs. Rectangles and arcs may be filled with a solid color. Rectangles may also be specified with rounded corners.

The coordinate system

The origin of the default coordinate system is at the upper left-hand corner of the screen or off-screen drawing area. The X-axis direction is positive towards the right, and the Y-axis direction is positive downwards. You can translate the origin to some other location if you want to by calling the translate method.

For example, by using the translate method and the appropriate arithmetic operations, you could cause the origin to be relocated to the center of the screen with the positive Y-axis direction going up the screen instead of down.

You can assume that horizontal and vertical distances in the coordinate system represent equal distances on the actual device display. In other words, if you draw a square, it won't be turned into a rectangle and if you draw a circle, it won't be turned into an ellipse.

All coordinates are specified as integers.

Drawing text

When a character is painted, the pixels forming the character's shape are filled with the Graphics object's current color. The pixels that are not part of the character's shape are left untouched. Several methods are available to draw text. As far as I know, you can only draw text horizontally.

Line stroke styles

Lines, arcs, rectangles, and rounded rectangles may be drawn with either a SOLID or a DOTTED stroke style. The default is SOLID. You can change the style by calling the setStrokeStyle method, which doesn't affect fill, text, and image operations.

Lines, arcs, rectangles, and rounded rectangles are drawn with a stroke that is one pixel wide. If you need additional width, you must draw two or more lines in parallel.

Clipping

Sun tells us,

"The clip is the set of pixels in the destination of the Graphics object that may be modified by graphics rendering operations."

To make a long story short, there is a single clip per Graphics object, and it is a rectangle that you can set with a call to the setClip method. The only pixels modified by graphics operations are those that lie within the clip rectangle. Pixels outside the clip rectangle are not modified by any graphics operations.

Anchor points

One of the more complex aspects of the MIDP 2.0 graphics drawing system is the concept of anchor points. Anchor points apply when you are drawing text, images, regions, and areas. (They do not apply when drawing lines, arcs, rectangles, and rounded rectangles.)

Later, I will present and explain a sample MIDlet named Canvas01 that illustrates what is meant by anchor points in conjunction with the drawing of text. (The interpretation is generally the same when drawing images, etc.) The output from the MIDlet, when run in the Sun cell phone emulator, is shown in Figure 2. I will explain the output from the MIDlet here, and explain the code that produced that output later.

Figure 2. Output from MIDlet named Canvas01 in Sun emulator.

No anchor point in J2SE graphics
The anchor parameter is not used in the argument list of the drawString method of J2SE version 1.6.

Drawing parameters

When you call the drawString method to draw text on the screen, you must provide the following four parameters:

  • String string - the text string to be drawn.
  • int x - a horizontal coordinate value.
  • int y - a vertical coordinate value.
  • int anchor - an anchor point value.

The big question...

The parameter values x and y clearly specify a point in the Cartesian coordinate system. The big question is what does the location of that point in the coordinate system have to do with the location of the text (or the image, etc.)?

Allowable anchor-point values

The allowable anchor-point values are given below in terms of public static final variables (constants) of the Graphics class:

Horizontal Anchor-Point Values

  • LEFT
  • HCENTER
  • VCENTER
  • RIGHT

Vertical Anchor-Point Values

  • TOP
  • BASELINE
  • BOTTOM

(Note that VCENTER can be used with images, but cannot be used with text. BASELINE can be used with text but cannot be used with images.)

Must construct a bitwise inclusive OR value

The value that is passed as the anchor parameter to the drawString method must be constructed as the bit-wise inclusive OR of one of the horizontal anchor-point values (excluding VCENTER for text) and one of the vertical anchor-point values (excluding BASELINE for images). The value zero may also be passed as the anchor parameter and is interpreted the same as the value that would be constructed from TOP | LEFT.

Eight calls to the drawString method

Now turn your attention to Figure 2. When we examine the code later, you will see that this MIDlet called the drawString method eight times in succession.  Each time the method is called the string that is passed as the first parameter is the text displayed on a single line on the screen in Figure 2. The value that is passed as anchor is constructed according to that text (exclusive of the part of the text that reads "y-," more on this later).

For example, the first call to the drawString method constructs the anchor value as TOP|RIGHT and the text in Figure 2 reads y-TOP|RIGHT.

The coordinate values for the first call to drawString

The x and y coordinate values passed to the method specify the point identified by the intersection of two lines immediately above the text at the right end of the text. In other words, the specified value for anchor instructs the system to display the text string below and to the left of the point at the specified coordinate values.

Compare intersections of lines with the text

If you follow the lines down the screen and compare the intersection of each pair of two lines with the construction of the anchor value given by the text, you should be able to see the correlation between the location of the specified point in the coordinate system and the location of the text string relative to that point.

TOP

Note that in all cases that use TOP for the construction of anchor, there is some space between the actual top of the upper case characters and the vertical location of the point in the coordinate system.

BOTTOM

Note also that in all cases that use BOTTOM for the construction of anchor there is some space between the bottom of the characters and the vertical location of the point. This is true even when the text includes a lower case y with a descender.

BASELINE

However, when BASELINE is used for the construction of anchor, there is no space between the bottom of the characters and the vertical location of the point. In other words, when BASELINE is used for the vertical placement, that is interpreted to mean that the bottom of the characters should coincide with that vertical coordinate with no blank space in between. When there are no descenders, the bottoms of the characters without descenders match the BASELINE value. When there are descenders, the bottom of the lowest descender matches the BASELINE value.

On the other hand, both BOTTOM and TOP allow for some blank space at the bottom and top of the characters.

RIGHT, LEFT, and HCENTER

RIGHT and LEFT mean what they say. As you can see, no blank space margin is allowed. HCENTER means that the horizontal center of the text should occur at the specified horizontal coordinate.

Interpretation for images

As mentioned earlier, the same concept applies to the placement of images with the added benefit of being able to use VCENTER to specify that the image should be centered vertically on the specified vertical coordinate value. (I will illustrate this in one of the sample MIDlets in this lesson.) On the other hand, BASELINE cannot be used with images.

Preview

I will present and explain two sample MIDlets named Canvas01 and Canvas02 in Part 1 of this lesson, and will present and explain a third sample MIDlet named Canvas03 in Part 2 of this lesson.

Canvas01

The purpose of the MIDlet named Canvas01 is to illustrate the use of aCanvas object and a Graphics object in a very simple and understandable way.

Canvas02

The purpose of the MIDlet named Canvas02 is to illustrate the detection and handling of key events on a Canvas object.  The MIDlet also demonstrates certain aspects of the full-screen mode versus the normal mode.

Canvas03

The purpose of this MIDlet named Canvas03 is to illustrate the following major programming concepts involving the use of the Canvas class and the Graphics class:

  • Creating an off-screen image.
  • Importing Image files and drawing them in specified locations on the off-screen image.
  • Drawing a variety of shapes in different colors on the off-screen image, including the following:
    • Rectangle
    • Filled triangle
    • Full circular arc
    • Rounded rectangle
  • Drawing the off-screen image onto the Canvas in a specified location.
  • Handling keyPressed events on the Canvas to cause the off-screen image to be drawn at different locations on the Canvas as a result of the user having pressed the following keys (simple animation):
    • RIGHT
    • LEFT
    • UP
    • DOWN
    • SELECT

In addition, the MIDlet named Canvas03 implements an EXIT command that can be used to terminate the MIDlet and cause it to enter the destroyed state.

Discussion and sample code

 
MIDlet testing
All of the MIDlets in this lesson were tested using a Java SE 6 compiler, targeted at a V1.4 virtual machine, and WTK 2.5.2 running under Windows XP.

The MIDlet named Canvas01

The purpose of this MIDlet is to illustrate the use of a Canvas object and a Graphics object in a very simple and understandable way.

Anchor point constants

The MIDlet further illustrates the use of anchor point constants to establish the alignment of drawn text. The output from the MIDlet running in the Sun cell phone emulator is shown in Figure 2.

Clear the screen

The MIDlet begins by painting the entire screen white. Otherwise, the new drawing would appear on top of what was previously on the screen. Then it sets the drawing color to black.

Draw the text and the lines

Following that, the MIDlet draws eight pairs of graphic objects. Each pair consists of:

  • A line from 0,0 to a specific location on the canvas.
  • A string of text with the anchor point coordinates set to match the end of the line.

The text contents of each string give the combination of anchor point constants used to position that particular string. Some text strings also begin with a lower case y- to show the effect of descenders on the vertical alignment of the text string.

The anchor point constants

The eight pairs of graphic objects illustrate the effect of different combinations of the following anchor point constants as well as the impact of lower-case descenders on the alignment produced by the constants:

  • TOP
  • BASELINE
  • BOTTOM
  • LEFT
  • HCENTER
  • RIGHT

Combining anchor point constants

An anchor point must be specified for each text string that is drawn. The anchor point must be produced by the bitwise inclusive OR of one of the top three (vertical) anchor constants in the above list and one of the bottom three (horizontal) anchor constants. For example, the following would be a valid anchor point: value to pass to the drawString method:

TOP|RIGHT

Determining the position and the alignment of the text

The actual position and the alignment of the text string are determined by the combination of a pair of specified coordinate values and the bitwise inclusive OR of two anchor constants. The coordinates specify the position that will be assumed by a particular point in the text string. The bitwise inclusive OR of two anchor constants determines which point in the text string will be located at the specified coordinates.

The special anchor point value of 0 is interpreted to mean TOP|LEFT.

Two more straight lines

Another pair of straight lines is used to connect the ends of the sloping lines that originate in the upper left corner of the screen.  This makes it easier to visually determine the location of the anchor point in each text string.

Methods that are used

This MIDlet illustrates the use of the following methods from the Graphics class:

  • setColor
  • fillRect
  • drawLine
  • drawString

The MIDlet also illustrates the use of the following methods from the Canvas class:

  • paint
  • repaint
  • getWidth
  • getHeight

Display for twenty seconds

The MIDlet displays the canvas for 20 seconds to give you a chance to view it and then enters the destroyed state.

The main class for the MIDlet named Canvas01

The MIDlet class is shown in its entirety in Listing 1.

Listing 1. The main class for the MIDlet named Canvas01.

public class Canvas01 extends MIDlet{
  Canvas myCanvas;

  public Canvas01(){
    System.out.println("Construct MIDlet");
    myCanvas = this.new MyCanvas();
    //Guarentee that the screen gets painted.
    myCanvas.repaint();
  }//end constructor

  public void startApp(){

    //Make the Canvas the current display.
    Display.getDisplay(this).setCurrent(myCanvas);

    //Sleep for 20 seconds.
    try{Thread.currentThread().sleep(20000);
    } catch(Exception e){}

    //Enter the destroyed state.
    this.destroyApp(true);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
    System.out.println("Destroy MIDlet");
    notifyDestroyed();
  }//end destroyApp

If you have studied the previous lessons in this series, the only new material in Listing 1 should be:

  • The instantiation of an object of a member class named MyClass.
  • The call to the repaint method belonging to that object.

Java graphics 101

As you will see later, the new class overrides the paint method inherited from the Canvas class. The code in the paint method produces the graphics on the screen. However, the paint method is a callback method and MIDlet code normally doesn't call it directly (although there may be exceptions to this rule of thumb). Rather, the MIDlet code normally calls the repaint method. This sends a request to the virtual machine to execute the paint method. This allows the virtual machine and the operating system to act as a traffic cop forcing the different applications needing access to the screen to play nice and take turns.

The call to the repaint method in Listing 1 is probably superfluous. The screen would probably be painted by executing the paint method anyway with or without this call to repaint. The call to repaint was included in Listing 1 simply to guarantee that the paint method does get called when the MIDlet becomes active. (Another sample MIDlet in this lesson illustrates the requirement to call the repaint method to cause the screen to be repainted with new information.)

No requirement for a member class
The class named MyCanvas was defined as a member class for programming convenience only. There was no technical requirement for making it a member class.

Beginning of the member class named MyCanvas

Listing 2 shows the beginning of the member class named MyCanvas. This class extends the Canvas class and overrides the paint method.

Listing 2. Beginning of the member class named MyCanvas.

  class MyCanvas extends Canvas{
    public void paint(Graphics g){
      //Paint the screen white
      g.setColor(0xffffff);
      g.fillRect(0,0,getWidth(),getHeight());

      //Set the drawing color to black.
      g.setColor(0x000000);

Clear the screen

Listing 2 begins by painting the entire screen white. Otherwise, the new drawing would simply appear over top of what was previously on the screen. Even though that was not desired in this MIDlet, drawing on top of the existing material on the screen can sometimes be useful. For example, that would make it possible to combine a TextBox object and a Canvas object on the same screen.

No Color class available
Unlike J2SE, MIDP 2.0 does not provide a Color class containing constants for use in setting color values. Therefore, you must set the color using a mixture of red, green, and blue values as shown in Listing 2.

Set the drawing color

After painting the screen white, Listing 2 sets the drawing color to black. This color will be used for all subsequent drawing until the drawing color is changed to some other color.

Draw eight graphic objects

Listing 3 draws the eight lines that originate in the upper left corner along with the eight text strings shown in Figure 2.

Listing 3. Draw eight lines and eight text strings.

      g.drawLine(0,0,200,20);
      g.drawString("y-TOP|RIGHT",200,20,
                             Graphics.TOP|Graphics.RIGHT);

      g.drawLine(0,0,150,40);
      g.drawString("y-TOP|HCENTER",150,40,
                           Graphics.TOP|Graphics.HCENTER);

      g.drawLine(0,0,100,60);
      g.drawString("y-TOP|LEFT",100,60,
                              Graphics.TOP|Graphics.LEFT);

      g.drawLine(0,0,100,90);
      g.drawString("y-BOTTOM|LEFT",100,90,
                           Graphics.BOTTOM|Graphics.LEFT);

      g.drawLine(0,0,100,110);
      g.drawString("BOTTOM|LEFT",100,110,
                           Graphics.BOTTOM|Graphics.LEFT);

      g.drawLine(0,0,100,130);
      g.drawString("y-BASELINE|LEFT",100,130,
                         Graphics.BASELINE|Graphics.LEFT);

      g.drawLine(0,0,100,150);
      g.drawString("BASELINE|LEFT",100,150,
                         Graphics.BASELINE|Graphics.LEFT);

      g.drawLine(0,0,100,170);
      g.drawString("Special Case of Zero",100,170,0);

Drawing on a Graphics object

Note that the overridden paint method receives an incoming parameter of type Graphics known locally simply as g. This is a reference to an object of the class Graphics and you can think of the Graphics object as representing the screen. Whatever you draw on the Graphics object will be rendered on the screen.

The drawLine method of the Graphics class is called eight times to draw the eight lines that originate at the origin and end at a particular location on the screen.

The drawString method is also called eight times to draw the eight text strings with the anchor point coordinate values matching the ends of the corresponding lines. Each time the drawString method is called, two anchor point constants are combined using a bitwise inclusive OR to provide the value that is passed as the fourth parameter. As explained earlier, this value is used to determine how the text string will be positioned relative to the point defined by the specified coordinate values.

Draw two more lines

Listing 4 draws two additional lines that connect the ends of the sloping lines that originate in the upper left corner of the screen.

Listing 4. Draw two more lines.

      g.drawLine(200,20,100,60);
      g.drawLine(100,60,100,170);

    }//end overridden paint method
  }//end member class MyCanvas

}//end class Canvas01

Listing 4 also signals the end of the overridden paint method and the end of the class named Canvas01.

The MIDlet named Canvas02

The purpose of this MIDlet is to illustrate the detection and handling of key events on a Canvas object. The MIDlet also demonstrates certain aspects of the full-screen mode versus the normal mode.

A Canvas object is instantiated and set to full-screen mode. The inherited keyPressed method is overridden to:

  • Capture each user keystroke,
  • Convert the key code to a key name.
  • Add the key name to a Vector object.
  • Cause the screen to be repainted each time a keyPressed event occurs.

The paint method is overridden to:

  • Clear the canvas.
  • Extract each of the key names from the Vector.
  • Display the key names in two columns on the screen by using the drawString method to draw the key names on the Canvas object.

Sun cell phone emulator output for the MIDlet named Canvas02

Figure 3 shows an output from the MIDlet when run in the Sun cell phone emulator for a particular sequence of keystrokes on the emulator keypad.

Figure 3. Sun cell phone emulator output for the MIDlet named Canvas02.

Keys that don't fire events

You can terminate the MIDlet that is running in the emulator by clicking the button on the right that has a picture of an old-fashioned telephone receiver in a horizontal position. (When you point to this button, the picture of the telephone receiver should turn red.) This button doesn't fire a keyPressed event. (At least it doesn't fire one that you can handle).

The key on the left with the picture of the house also doesn't fire a keyPressed event.

The key pressed order

Figure 3 shows the result of starting at the top left soft key and pressing keys going from left to right, top to bottom, excluding the two keys mentioned above. The names of most of the keys in the Sun emulator are pretty easy to correlate with the label on the key. Two that may not be clear are SELECT and CLEAR. The SELECT key is the large square key in the middle of the arrow keys. The CLEAR key is the key on the right with the picture of the audio speaker.

Comparing full-screen and normal mode

You can see one of the differences between full-screen mode and normal mode by comparing Figure 3, (which is in full-screen mode) and Figure 2, (which is in normal mode). Since neither of these MIDlets displays a title and a ticker, the main difference between Figure 2 and Figure 3 is the disappearance of the gray bar at the bottom of the screen in Figure 3 where soft key commands normally appear. (I will have more to say about this later.)

Beginning of the class for the MIDlet named Canvas02

Listing 5 shows the beginning of the class for the MIDlet named Canvas02 including the constructor.

Listing 5. Beginning of the class for the MIDlet named Canvas02.

public class Canvas02 extends MIDlet{
  Canvas myCanvas;  

  public Canvas02(){//Constructor
    System.out.println("Construct MIDlet");
    myCanvas = this.new MyCanvas();
    //Guarentee that the screen gets painted.
    myCanvas.repaint();

    //Add three commands to the Canvas
    myCanvas.addCommand(new Command("EXIT",
                                         Command.EXIT,2));
    myCanvas.addCommand(new Command("BACK",
                                         Command.BACK,2));
    myCanvas.addCommand(new Command("OK",Command.OK,2));
  }//end constructor

Three commands are added

Note that the constructor in Listing 5 adds three commands to the Canvas object. However, even if visible, the commands could not be activated because there is no CommandListener registered on the Canvas object. The commands were added to the Canvas solely to demonstrate the impact of full-screen mode versus normal mode. As mentioned earlier, when the Canvas object is set to full-screen mode, as is the case for this MIDlet, the commands are not visible.

Alternative command presentation
The Sun documentation indicates that in full-screen mode, "Commands may be presented using some alternative means (such as through a pop-up menu)." If the Sun cell phone emulator provides such an alternative presentation, I haven't figured out what it is.

Accessing the commands

If the Canvas is switched to normal mode, the commands are displayed on the gray bar that normally appears at the bottom of the screen and also in a command menu accessible from the gray bar. You can demonstrate this behavior by modifying the code to pass true or false to the setFullScreenMode method in the MyCanvas constructor that I will discuss later.

Remainder of the main class for the MIDlet named Canvas02

Listing 6 shows the remainder of the main class for the MIDlet named Canvas02.

Listing 6. Remainder of the main class for the MIDlet named Canvas02.

  public void startApp(){
    //Make the Canvas the current display.
    Display.getDisplay(this).setCurrent(myCanvas);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
    System.out.println("Destroy MIDlet");
    notifyDestroyed();
  }//end destroyApp

There is nothing new in this code so further explanation should not be necessary.

Beginning of the member class named MyCanvas

Listing 7 shows the beginning of the member class named MyCanvas, including the constructor.

Listing 7. Beginning of the member class named MyCanvas.

  class MyCanvas extends Canvas{
    Vector vect = new Vector();

    MyCanvas(){//constructor
      this.setFullScreenMode(true);
    }//end constructor

The constructor calls the method named setFullScreenMode to cause the Canvas to appear on the screen in full-screen mode. If you change the parameter from true to false, the Canvas will revert to normal mode. If what you need is normal mode, you don't need to call this method because the Canvas is in normal mode by default.

Beginning of the overridden paint method

Listing 8 shows the beginning of the overridden paint method. The method begins by painting the screen white and then setting the drawing color to black. Following this, the method declares some working variables and initializes some of them.

Listing 8. Beginning of the overridden paint method.

    public void paint(Graphics g){
      //Paint the entire screen white.
      g.setColor(0xffffff);
      g.fillRect(0,0,getWidth(),getHeight());

      g.setColor(0x000000);//Set drawing color to black

      //Declare and initialize some working variables.
      String keyData = null;
      int fontHeight = g.getFont().getHeight();
      //Number of lines of text that fit on screen.
      int cntLimit = this.getHeight()/fontHeight;
      int yOffset = 0;
      int xOffset = 0;

A two-column display

Most of the complexity of this method resides in the requirement to create two columns for drawing the text strings. This requires knowledge of the number of strings that will fit from top to bottom on the screen, which in turn requires knowledge of the vertical space occupied by each character. The code in Listing 8 gets the font height and performs that calculation.

Remainder of the overridden paint method

Listing 9 displays the data captured from the user keyboard input in two columns. When the length of the first column exceeds the height of the screen, the display wraps and starts a new column as shown in Figure 3. If the length of the second column exceeds the height of the screen, the data will simply be lost off the bottom of the screen.

Listing 9. Remainder of the overridden paint method.

      for(int cnt = 0;cnt < vect.size();cnt++){
        keyData = (String)vect.elementAt(cnt);//note cast

        if(cnt * fontHeight < this.getHeight() - 
                                              fontHeight){
          //Display in first column.
          yOffset = cnt * fontHeight;
          xOffset = 5;
        }else{
          //Display in second column
          yOffset = (cnt - cntLimit) * fontHeight;
          xOffset = this.getWidth()/2;
        }//end else
        
        //Draw the string on the Canvas.
        g.drawString(keyData,xOffset,yOffset,0);
      }//end for loop

    }//end overridden paint method

Drawing from top to bottom of the screen

Note that a value of 0 is passed as the fourth parameter to the drawString method. This causes the anchor point location to default to TOP|LEFT.

Since the direction for increasing values of the Y-coordinate is down the screen, the code in Listing 9 extracts the text data from the Vector and draws the text on the screen moving from top to bottom.

The keyReleased method
When the MyCanvas object is on the screen and the user releases one of the keys, the keyReleased method is called. In this MIDlet, the empty keyReleased method is not overridden. Calling the empty method is of no consequence. It simply returns quietly.

Overridden keyPressed method

The MyCanvas class inherits an empty method named keyPressed from the Canvas class and overrides that method. Whenever the MyCanvas object is being displayed on the screen and the used presses one of the keys shown in Figure 3, the Canvas object "fires" a keyPressed event, causing the overridden keyPressed method to be called. The overridden keyPressed method is shown in Listing 10.

Listing 10. Overridden keyPressed method.

    public void keyPressed(int keyCode){
      vect.addElement(getKeyName(keyCode));
      this.repaint();
    }//end keyPressed
  }//end member class MyCanvas

}//end class Canvas02

Behavior of the code in Listing 10

The code in Listing 10:

  • Gets the name of the key that was pressed (which may be peculiar to the Sun cell phone emulator).
  • Adds that name to the data already in the Vector object.
  • Calls the repaint method on the Canvas object.

The call to the repaint method causes the overridden paint method to be executed. As described earlier, the call to the overridden paint method:

  • Clears the canvas by painting it white.
  • Extracts the text data from the Vector (including the new data).
  • Draws the text on the screen in two columns as shown in Figure 3.

Therefore, each time the user presses a key, the screen is re-drawn, causing the contents of the Vector object to be displayed. In this case, the call to the repaint method is not superfluous as it probably was earlier.

When is the paint method called?

As mentioned above, the paint method is called whenever the program code calls the repaint method. However, that is not the only time the paint method is called. It is called at any time that the system needs to cause the Canvas to be repainted by the MIDlet for any reason. For example, if a system screen covers the Canvas temporarily, the paint method will be called to redraw the screen when the system screen goes away.

Run the programs

I encourage you to copy the MIDlet code from Listing 11 and Listing 12. Run the MIDlets in the updated MIDlet development framework named WTKFramework03 that I provided in the lesson titled Using Alerts, Images, Timers, and Gauges in MIDlets (see Resources). Experiment with the MIDlet code, making changes and running your modified MIDlets in the framework program. See if you can explain the results produced by your changes.

Don't forget that you will need to download and install the latest version of the Sun Java Wireless Toolkit for CLDC (see Resources). As of the date this lesson is being written, the latest version of the toolkit is WTK2.5.2.

Summary

In this lesson, you learned how to use several methods of the Canvas class (including paint and repaint), along with several methods of the Graphics class to draw text and lines on the cell phone screen. You learned how to use anchor point coordinates to control the position of text that is drawn on the screen. You also learned how to handle keyPressed events fired by a Canvas object.

What's next?

In Part 2 of this lesson, you will learn how to mix image file data and drawn graphics on a cell phone screen. You will learn how to draw various shapes such as rectangles, circles, arcs, filled triangles, and rounded rectangles. You will learn how to use an off-screen image, and how to use the event handling capability of the Canvas class for simple animation. You will also learn how to make you MIDlet animations efficient by minimizing the screen area that must be repainted.

Resources

Complete program listings

Complete listings of the programs discussed in this lesson are shown in Listing 11 and Listing 12 below.

Listing 11. Source code for the MIDlet named Canvas01.

/*Canvas01.java
Copyright 2007, R.G.Baldwin

The purpose of this program is to illustrate a simple 
MIDlet using a Canvas object and a Graphics object. It
further illustrates the use of anchor point constants to
establish the alignment of drawn text.

The MIDlet begins by painting the entire screen white.
Otherwise, the new drawing would appear on top of what
was previously on the screen.

Then it sets the drawing color to black.

Then it draws eight pairs of graphic objects.  Each pair 
consists of:

A line from 0,0 to a specific coordinate value on the
 canvas.
A string of text with the anchor point set to match the
 end of the line. The text contents of each string give
 the combination of anchor point constants used to
 position that particular string. Some text strings also
 begin with a lower case y- to show the effect of
 descenders on the alignment of the text string.

The eight pairs of graphic objects illustrate the effect 
of different combinations of the following anchor point 
constants as well as the impact of lower-case descenders
on the alignment produced by the constants:

TOP
BASELINE
BOTTOM

LEFT
HCENTER
RIGHT

An anchor point must be specified for each text string 
that is drawn. The anchor point must be produced by the
bitwise inclusive OR of one of the top three vertical 
anchor constants and one of the bottom three horizontal 
anchor constants. For example, the following would be a
valid anchor point:

TOP|RIGHT

The actual position and the alignment of the text string 
are determined by the combination of a pair of specified 
coordinate values and the bitwise inclusive OR of two 
anchor constants. The coordinates specify the position 
that will be assumed by a particular point in the text 
string. The bitwise inclusive OR of two anchor constants
determines which point in the text string will be located
at the specified coordinates.

The special anchor point value of zero is interpreted to 
mean TOP|LEFT.

A pair of straight lines is used to connect the ends of
two groups of sloping lines.  This makes it easier to
visually determine the location of the anchor point in
each text string.

This MIDlet illustrates the use of the following methods
from the Graphics class:

setColor
fillRect
drawLine
drawString

It also illustrates the use of the following method from
the Canvas class:

paint
repaint
getWidth
getHeight

The MIDlet displays the canvas for 20 seconds and then
enters the destroyed state.

Tested using a Java SE 6 compiler, targeted at a V1.4
virtual machine, and WTK 2.5.2 running under Windows XP.
*********************************************************/

package Canvas01;

import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

public class Canvas01 extends MIDlet{
  Canvas myCanvas;

  public Canvas01(){
    System.out.println("Construct MIDlet");
    myCanvas = this.new MyCanvas();
    //Guarentee that the screen gets painted.
    myCanvas.repaint();
  }//end constructor

  public void startApp(){

    //Make the Canvas the current display.
    Display.getDisplay(this).setCurrent(myCanvas);

    //Sleep for 20 seconds.
    try{Thread.currentThread().sleep(20000);
    } catch(Exception e){}

    //Enter the destroyed state.
    this.destroyApp(true);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
    System.out.println("Destroy MIDlet");
    notifyDestroyed();
  }//end destroyApp
  //----------------------------------------------------//

  //member class
  class MyCanvas extends Canvas{
    public void paint(Graphics g){
      //Paint the entire screen white. Otherwise, the new
      // drawing will simply appear over top of what was
      // previously on the screen.
      g.setColor(0xffffff);
      g.fillRect(0,0,getWidth(),getHeight());

      //Set the drawing color to black.
      g.setColor(0x000000);

      //Draw text at eight different anchor points. The
      // text shows the vertical and horizontal constants
      // required to set the respective anchor points.
      // In addition, a line is drawn from 0,0 to the
      // anchor point to visually identify the anchor
      // point. A lower-case y is used to show the effect
      // of descenders on the alignment of the text.
      g.drawLine(0,0,200,20);
      g.drawString("y-TOP|RIGHT",200,20,
                             Graphics.TOP|Graphics.RIGHT);

      g.drawLine(0,0,150,40);
      g.drawString("y-TOP|HCENTER",150,40,
                           Graphics.TOP|Graphics.HCENTER);

      g.drawLine(0,0,100,60);
      g.drawString("y-TOP|LEFT",100,60,
                              Graphics.TOP|Graphics.LEFT);

      g.drawLine(0,0,100,90);
      g.drawString("y-BOTTOM|LEFT",100,90,
                           Graphics.BOTTOM|Graphics.LEFT);

      g.drawLine(0,0,100,110);
      g.drawString("BOTTOM|LEFT",100,110,
                           Graphics.BOTTOM|Graphics.LEFT);

      g.drawLine(0,0,100,130);
      g.drawString("y-BASELINE|LEFT",100,130,
                         Graphics.BASELINE|Graphics.LEFT);

      g.drawLine(0,0,100,150);
      g.drawString("BASELINE|LEFT",100,150,
                         Graphics.BASELINE|Graphics.LEFT);

      g.drawLine(0,0,100,170);
      g.drawString("Special Case of Zero",100,170,0);

      //Draw straight lines that connect the ends of the
      // sloping lines.
      g.drawLine(200,20,100,60);
      g.drawLine(100,60,100,170);

    }//end overridden paint method
  }//end member class MyCanvas

}//end class Canvas01

 

Listing 12. Source code for the MIDlet named Canvas02.

/*Canvas02.java
Copyright 2007, R.G.Baldwin

The purpose of this program is to illustrate the 
detection and handling of key events on a Canvas object.
The MIDlet also demonstrates certain aspects of the
FullScreenMode versus the normal mode.

A Canvas object is instantiated and set to FullScreenMode.

The inherited keyPressed method is overridden to capture
a user keystroke, convert the key code to a key name, add
the key name to a Vector object, and cause the screen to
be repainted each time a keyPressed event occurs.

The paint method is overridden to clear the canvas,
extract all of the key names from the Vector, and display
the key names in two columns by using the drawString
method to draw the key names on the Canvas object.

Note, on the Sun cell phone emulator, you can terminate
the MIDlet by clicking the button on the right that has
a picture of a horizontal telephone receiver. When you
point to this button, the picture of the telephone
receiver should turn red. I did not register an EXIT
command listener because I did not want to cause the
left soft key to be occupied with a command when in
normal mode.

Tested using a Java SE 6 compiler, targeted at a V1.4
virtual machine, and WTK 2.5.2 running under Windows XP.
*********************************************************/

package Canvas02;

import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import java.util.Vector;

public class Canvas02 extends MIDlet{
  Canvas myCanvas;

  public Canvas02(){
    System.out.println("Construct MIDlet");
    myCanvas = this.new MyCanvas();
    //Guarentee that the screen gets painted.
    myCanvas.repaint();

    //Note: The following commands cannot be activated
    // because there is no CommandListener registered on
    // the Canvas object. They are here solely to
    // demonstrate the impact of FullScreenMode versus
    // normal mode. When the Canvas object is set to
    // FullScreenMode, as is the case in this MIDlet,
    // these commands are not displayed. Therefore, even
    // if a CommandListener were registered on the Canvas,
    // it would not be possible for the user to activate
    // the commands.  In normal mode, the commands are
    // displayed. If there was a CommandListener
    // registered on the Canvas, the user could activate
    // the commands. You can demonstrate this behavior by
    // modifying the code to pass true or false to the
    // setFullScreenMode method in the MyCanvas
    // constructor. 
    myCanvas.addCommand(new Command("EXIT",
                                         Command.EXIT,2));
    myCanvas.addCommand(new Command("BACK",
                                         Command.BACK,2));
    myCanvas.addCommand(new Command("OK",Command.OK,2));
  }//end constructor

  public void startApp(){
    //Make the Canvas the current display.
    Display.getDisplay(this).setCurrent(myCanvas);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
    System.out.println("Destroy MIDlet");
    notifyDestroyed();
  }//end destroyApp
  //----------------------------------------------------//

  //Member class
  class MyCanvas extends Canvas{
    Vector vect = new Vector();

    MyCanvas(){//constructor
      //Note: In normal mode, the gray bar where the soft
      // key commands normally appear is visible at the
      // bottom of the screen.  However, in
      // FullScreenMode, that gray bar disappears. This
      // seems it make it impossible for the user to
      // activate commands on the Canvas when in
      // FullScreenMode. You can demonstrate this behavior
      // by either removing the following statement or
      // modifying the code to pass false to the
      // setFullScreenMode method. Passing false to the
      // method will switch the Canvas to normal mode.
      this.setFullScreenMode(true);
    }//end constructor

    public void paint(Graphics g){
      //Paint the entire screen white.
      g.setColor(0xffffff);
      g.fillRect(0,0,getWidth(),getHeight());

      g.setColor(0x000000);//Set drawing color to black

      //Declare and initialize some working variables.
      String keyData = null;
      int fontHeight = g.getFont().getHeight();
      //Number of lines of text that fit on screen.
      int cntLimit = this.getHeight()/fontHeight;
      int yOffset = 0;
      int xOffset = 0;

      //Display the data captured from the user keyboard
      // input in two columns using graphic display
      // capabilities. If the length of the second
      // column exceeds the height of the screen, the
      // data will simply fall off the bottom of the
      // screen.
      for(int cnt = 0;cnt < vect.size();cnt++){
        keyData = (String)vect.elementAt(cnt);//note cast

        if(cnt * fontHeight < this.getHeight() - 
                                              fontHeight){
          //Display in first column.
          yOffset = cnt * fontHeight;
          xOffset = 5;
        }else{
          //Display in second column
          yOffset = (cnt - cntLimit) * fontHeight;
          xOffset = this.getWidth()/2;
        }//end else

        //Draw the string on the Canvas.
        g.drawString(keyData,xOffset,yOffset,0);
      }//end for loop

    }//end overridden paint method
    //--------------------------------------------------//

    //Override the keyPressed method to:
    // capture keystrokes
    // save them in a Vector object
    // repaint the canvas.
    public void keyPressed(int keyCode){
      vect.addElement(getKeyName(keyCode));
      this.repaint();
    }//end keyPressed
  }//end member class MyCanvas

}//end class Canvas02

 


Copyright

Copyright 2008, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com






Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel