YOU ARE HERE: Home > Tech > Java J2ME > Article

MIDP Programming (Part II)
By SAMS This article was not rated yet.
 
Printer Version Printer Friendly | Add As Favorite | Link to Article (Page 1 of 2)|1||2|Next

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 lcdui coordinate system.

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 below 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.


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 below shows the anchor points for the valid constant combinations.


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);
?? }
??}



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 below 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.


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 below shows the output of the FontDemo example.


Output of the FontDemo example.

Images

The Graphics class also provides a method for drawing images. As shown in the final version of TeleTransfer application, Images can be predefined and contained in the JAR file of the MIDlet. The only file format that is mandatory for MIDP is the Portable Network Graphics (PNG) file format. The PNG format has several advantages over other graphics formats; for example, it is license free and supports true color images, including a full transparency (alpha) channel. PNG images are always compressed with a loss-less algorithm. The algorithm is identical to the algorithm used for JAR files, so the MIDP implementation can save space by using the same algorithm for both purposes.

An image can be loaded from the JAR file using the static method Image.create (String name). The name parameter denotes the filename of the image in the JAR file. Please note that this create() method may throw an IOException.

The drawImage() method in Graphics requires an Image object, the coordinates, and an integer denoting the alignment as parameters. The alignment parameter is similar the alignment of drawString(), except that the BASELINE constant is not supported. An additional alignment constant available for images only is VCENTER, which forces the image to be vertically centered relative to the given coordinates. Figure below shows the valid constant combinations and the corresponding anchor points.


Alignment constant combinations valid for images and the corresponding anchor points.

The following example first loads the image logo.png from the MIDlet JAR file in the constructor, and then displays the image three times. One image is drawn in the upper-left corner, one in the lower-right corner, and one in the center of the display, as shown in next Figure:


??import java.io.*;
??import javax.microedition.midlet.*;
??import javax.microedition.lcdui.*;

??class ImageDemoCanvas extends Canvas {

?? Image image;

?? public ImageDemoCanvas () {
?? try {
?? image = Image.createImage ("/logo.png");
?? }
?? catch (IOException e) {
?? throw new RuntimeException ("Unable to load Image: "+e);
?? }
?? }

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

?? g.drawImage (image, 0, 0, Graphics.TOP | Graphics.LEFT);
?? g.drawImage (image, getWidth () / 2, getHeight () / 2,
?? Graphics.HCENTER | Graphics.VCENTER);
?? g.drawImage (image, getWidth (), getHeight (),
?? Graphics.BOTTOM | Graphics.RIGHT);
?? }
??}



Output of the ImageDemo example.

Images can also be created at runtime from scratch. The static method Image.create (int width, int height) creates a new dynamic image of the given size. In contrast to images loaded from a JAR file, these images are mutable. Mutable images can be modified by calling getGraphics (). The Graphics object returned can be used for modifying the image with all the methods provided by the Graphics class. Please note that images loaded from a JAR file cannot be modified. However, it is possible to create a mutable image, and then draw any other image in the mutable image.

By modifying the constructor of the previous example canvas as follows, the image drawn in the paint() method is created and filled at runtime instead of loading an image from the JAR file:


??public ImageDemoCanvas () {
?? image = Image.createImage (10,10);
?? image.getGraphics ().fillArc (0,0,10,10,0, 360);
??}

The disadvantage of mutable images is that they cannot be used in high-level GUI elements since it is possible to modify them at any time, possibly leading to inconsistent display of widgets. For that reason, another static create method, createImage(Image image), is provided that creates an immutable image from another image.

Interaction

Because the Canvas class is a subclass of Displayable, it provides the same support for commands as the high-level screen classes. Here, you will concentrate on the additional interaction possibilities the Canvas class offers: direct key input and pointer support.

Please note that all input events and command notifications and the paint() method are called serially. That means that the application manager will call none of the methods until the previous event handling method has returned. So all these methods should return quickly, or the user will be unable to interact with the application. For longer tasks, a separate thread can be started.

Key Input

For key input, the Canvas class provides three callback methods: keyPressed(), keyReleased(), and keyRepeated(). As the names suggest, keyPressed() is called when a key is pressed, keyRepeated() is called when the user holds down the key for a longer period of time, and keyReleased() is called when the user releases the key.

All three callback methods provide an integer parameter, denoting the Unicode character code assigned to the corresponding key. If a key has no Unicode correspondence, the given integer is negative. MIDP defines the following constant for the keys of a standard ITU-T keypad: KEY_NUM0, KEY_NUM1, KEY_NUM2, KEY_NUM3, KEY_NUM4, KEY_NUM5, KEY_NUM6, KEY_NUM7, KEY_NUM8, KEY_NUM9, KEY_POUND, and KEY_STAR. Applications should not rely on the presence of any additional key codes. In particular, upper- and lowercase or characters generated by pressing a key multiple times are not supported by low-level key events. A "name" assigned to the key can be queried using the getKeyName() method.

Some keys may have an additional meaning in games. For this purpose, MIDP provides the constants UP, DOWN, LEFT, RIGHT, FIRE, GAME_A, GAME_B, GAME_C, and GAME_D. The "game" meaning of a keypress can be determined by calling the getGameAction() method. The mapping from key codes to game actions is device dependent, so different keys may map to the same game action on different devices. For example, some devices may have separate cursor keys; others may map the number pad to four-way movement. Also, several keys may be mapped to the same game code. The game code can be translated back to a key code using the getKeyCode() method. This also offers a way to get the name of the key assigned to a game action. For example, the help screen of an application may display

??"press "+getKeyName (getKeyCode (GAME_A))
instead of "press GAME_A".

The following canvas implementation shows the usage of the key event methods. For each key pressed, repeated, or released, it shows the event type, character and code, key name, and game action.

The first part of the implementation stores the event type and code in two variables and schedules a repaint whenever a key event occurs:


??import javax.microedition.lcdui.*;

??class KeyDemoCanvas extends Canvas {

?? String eventType = "- Press any!";
?? int keyCode;

?? public void keyPressed (int keyCode) {
?? eventType = "pressed";
?? this.keyCode = keyCode;
?? repaint ();
?? }

?? public void keyReleased (int keyCode) {
?? eventType = "released";
?? this.keyCode = keyCode;
?? repaint ();
?? }

?? public void keyRepeated (int keyCode) {
?? eventType = "repeated";
?? this.keyCode = keyCode;
?? repaint ();
?? }


The second part prints all event properties available to the device screen. For this purpose, you first implement an additional write() method that helps the paint() method to identify the current y position on the screen. This is necessary because drawText() does not advance to a new line automatically. The write() method draws the string at the given y position and returns the y position plus the line height of the current font, so paint() knows where to draw the next line:

??public int write (Graphics g, int y, String s) {
?? g.drawString (s, 0, y, Graphics.LEFT|Graphics.TOP);
?? return y + g.getFont ().getHeight ();
??}

The paint() method analyzes the keyCode and prints the result by calling the write() method defined previously, as shown in Figure below.


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

?? g.setGrayScale (0);

?? int y = 0;
?? y = write (g, y, "Key "+ eventType);
?? if (keyCode == 0) return;

?? y = write (g, y, "Char/Code: "+ ((keyCode < 0) ? "N/A" : ""
?? +(char) keyCode) + "/" + keyCode);
?? y = write (g, y, "Name: "+getKeyName (keyCode));
?? String gameAction;
?? switch (getGameAction (keyCode)) {
?? case LEFT: gameAction = "LEFT"; break;
?? case RIGHT: gameAction = "RIGHT"; break;
?? case UP: gameAction = "UP"; break;
?? case DOWN: gameAction = "DOWN"; break;
?? case FIRE: gameAction = "FIRE"; break;
?? case GAME_A: gameAction = "GAME_A"; break;
?? case GAME_B: gameAction = "GAME_B"; break;
?? case GAME_C: gameAction = "GAME_C"; break;
?? case GAME_D: gameAction = "GAME_D"; break;
?? default: gameAction = "N/A";
?? }
?? write (g, y, "Action: "+gameAction);
?? }
??}



Output of the KeyDemo example when the "Fire" key was released.

Pointer Events

For devices supporting a pointer device such as a stylus, touch screen, or trackball, the Canvas class provides three notification methods: pointerPressed(), pointerDragged(), and pointerReleased(). These methods work similarly to the key event methods, except that they provide two integer parameters, denoting the x and y position of the pointer when the corresponding event occurs. (Please note that pointer support is optional in MIDP, so the application should not rely on the presence of a pointer. Such devices are uncommon for devices such as mobile phones.) The following sample program demonstrates the usage of the three methods:


??import javax.microedition.lcdui.*;

??class PointerDemoCanvas extends Canvas {

?? String eventType = "Press Pointer!";
?? int x;
?? int y;

?? public void pointerPressed (int x, int y) {
?? eventType = "Pointer Pressed";
?? this.x = x;
?? this.y = y;
?? repaint ();
?? }

?? public void pointerReleased (int x, int y) {
?? eventType = "Pointer Released";
?? this.x = x;
?? this.y = y;
?? repaint ();
?? }

?? public void pointerDragged (int x, int y) {
?? eventType = "Pointer Repeated";
?? this.x = x;
?? this.y = y;
?? repaint ();
?? }

?? public void paint (Graphics g) {
?? g.setGrayScale (255);
?? g.fillRect (0, 0, getWidth (), getHeight ());
?? g.setGrayScale (0);
?? g.drawString (eventType + " " +x +"/"+y,
?? 0, 0, Graphics.TOP|Graphics.LEFT);
?? g.drawLine (x-4, y, x+4, y);
?? g.drawLine (x, y-4, x, y+4);
?? }
??}

Foreground and Background Notifications

For several reasons, the Canvas may move into the background?for example, if the display is set to another displayable object or if the device displays a system dialog. In these cases, the Canvas is notified by the hideNotify() method. When the Canvas becomes visible (again), the corresponding counterpart, showNotify(), is called.

Javagochi Example

Now that you are familiar with the Canvas object and the basic drawing methods of the Graphics class, you are ready to develop a small interactive application, the Javagochi.

As you can see in the following code, the MIDlet implementation of Javagochi is already finished, but the Face class is missing:


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

??public class Javagochi extends MIDlet {
??
?? static final int IDEAL_WEIGHT = 100;
?? Display display = Display.getDisplay (this);
?? Face face = new Face (this);
?? int weight = IDEAL_WEIGHT;
?? Timer consumption;
?? int score;


Before you begin development, let us first say a few words about the Javagochi itself. A Javagochi has a weight that is initialized with its IDEAL_WEIGHT. It also owns an instance of Display, Face, and Consumption, which will be explained later. Finally, it stores a score value for the care the owner spends on the Javagochi.

The happiness of the Javagochi is determined by the deviation of its current weight from the ideal weight, ranging from 10 to 0:


??public int getHappiness () {
?? return 20 - (weight > IDEAL_WEIGHT
?? ? 10 * weight / IDEAL_WEIGHT
?? : 10 * IDEAL_WEIGHT / weight);
?? if (happiness < 0) happiness = 0;
?? if (happiness > 10) happiness = 10;
??}

This formula also demonstrates how to circumvent problems with the absence of floating point arithmetic. In order to avoid loss of significant fractions, the values are scaled up before division.

Like all other known life forms, the Javagochi can die. Javagochies only die from sadness when their happiness level reaches zero:


??public boolean isDead () {
?? return getHappiness <= 0;
??}

The only other action a Javagochi can perform besides dying is to transform energy to matter and back. Since a weight change may change the Javagochi's look, a repaint is requested in the transform() method:

??public void transform (int amount) {
?? if (!isDead ()) {
?? weight += amount;
?? face.repaint ();
?? }
??}

When the Javagochi MIDlet is started, it displays itself and starts a consumption Timer that keeps track of the power the Javagochi needs for living:

??public void startApp () {
?? display.setCurrent (face);
?? consumption = new Consumption (this).start ();
??}

When the MIDlet is paused, the Javagochi goes to sleep by telling the consumption thread to terminate itself. The destroyApp() method does nothing because the life cycle will enter sleep anyway, and no further cleanup is needed:

 
??public void pauseApp () {
?? consumption.leave = true;
?? }

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


The consumption Thread is a separate class that monitors the power the Javagochi needs for living. In the run() method, every 0.5 seconds the score is updated depending on the Javagochi's happiness and the small amount of body mass that is transformed back to life energy:

??public class Consumption extends Thread {

?? Javagochi javagochi;
?? boolean leave = false;

?? public Consumption (Javagochi javagochi) {
?? this.javagochi = javagochi;
?? }

?? public void run () {
?? while (!leave) {
?? try {
?? sleep (500);
?? }
?? catch (InterruptedException e) {break;}
?? javagochi.score += 10 - javagochi.deviation;
?? javagochi.transform (-5);
?? }
?? }
??}


Now that you know how a Javagochi works, it is your job to give the Javagochi an appropriate appearance by implementing the missing Face class.

Scaling and Fitting

In many cases, it is a good idea to scale displayed graphics depending on the actual screen size. Otherwise, the display will look nice on one particular device type, but won't fit the screen on devices with a lower screen resolution or become unnecessarily small on devices with higher screen resolutions.

We will now show how scaling works for the Javagochi example. A picture of a Javagochi is shown in Figure below. You will start by drawing the shape of the face, a simple ellipse. In this case, the ellipse will reflect the Javagochi's weight. If the Javagochi is at its ideal weight, the ellipse becomes a circle.


A happy Javagochi at its ideal weight.

In order to leave some space for the Javagochi to grow, the diameter of the ideal circle is half the minimum of the screen width and height. Thus, the height of the Javagochi is calculated using the following formula:


??int height = Math.min (getHeight (), getWidth ()) / 2;

Based on the current weight, the ideal weight, and the calculated height, which is also the diameter of the "ideal" Javagochi, you can now calculate the width of the Javagochi:

??int width = height * javagochi.weight / javagochi.IDEAL_WEIGHT;

Other applications may of course have other dependencies from the actual screen size, but this example should be sufficient to show the general idea.

The Javagochi's skin color is dependent on its happiness. If the Javagochi feels well, its skin has a bright yellow color. With decreasing happiness, the Javagochi becomes pale. This is reflected by the following setColor() command:


??setColor (255, 255, 28 * javagochi.happiness);

Using the given width and height, you can now implement your first version of the Javagochi's Face class:

??import javax.microedition.lcdui.*;
??class Face extends Canvas implements CommandListener {
?? Javagochi javagochi;

?? Face (Javagochi javagochi) {
?? this.javagochi = javagochi;
?? }
??
?? public void paint (Graphics g) {
?? g.setColor (255, 255, 255);
?? g.fillRect (0, 0, getWidth (), getHeight ());
??
?? int height = Math.min (getHeight (), getWidth ()) / 2;
?? int width = height * javagochi.weight / javagochi.IDEAL_WEIGHT;
??
?? g.translate (getWidth () / 2, getHeight () / 2);
??
?? g.setColor (255, 255, 255 - javagochi.getHappiness () * 25);
?? g.fillArc (- width / 2, - height / 2, width, height, 0, 360);

?? g.setColor (0, 0, 0);
?? g.drawArc (- width / 2, - height / 2, width, height, 0, 360);
?? }
??}

In order to simplify the centered display of the Javagochi, you set the origin of the coordinate system to the center of the screen using the translate() method. The outline of the Javagochi's face is then drawn using the drawArc() method.

Unfortunately, the outline of the Javagochi looks a bit boring, so you will add a simple face now. In order to avoid duplicated code, you put the drawing of the eyes in a separate method. The drawEye() method takes the Graphics object, the coordinates of the eye, and a size parameter:


??void drawEye (Graphics g, int x, int y, int size) {
?? if (javagochi.isDead ()) {
?? graphics.drawLine (x - size/2, y, x + size/2, y);
?? graphics.drawLine (x, y - size/2, x, y + size/2);
?? }
?? else
?? graphics.drawArc (x-size/2, y-size/2, size, size, 0, 360);
??}

Now you can insert the rest of the drawing code into the paint() method, just after drawArc(). You will start with the eyes by calling the drawEye() method defined previously. By using fractions of the current width and height of the Javagochi, the eyes are positioned and sized correctly:

??drawEye (g, - width / 6, - height / 5, height / 15 + 1);
??drawEye (g, width / 6, - height / 5, height / 15 + 1);
Now you draw the mouth, depending on the current happiness of the Javagochi. Again, you use fractions of the Javagochi size for positioning and sizing:

??switch (javagochi.getHappiness () / 3) {
??case 0:
??case 1: g.drawArc (-width/6, height/7, width/3, height/6, 0, 180);
??break;
??case 2: g.drawLine (-width/6, height/7, width/6, height/7);
??break;
??default: g.drawArc (-width/6, height/7, width/3, height/6, 0, -180);
??}

Simple Interaction

When you run the first version of the Javagochi application, the Javagochi starts out happy, but dies quickly from starvation. Obviously, you need a way to transfer energy from the device's battery to the Javagochi. One possibility would be to add a corresponding command.

However, in the "High-Level API" section you learned that commands may be delegated to a sub-menu. When the Javagochi urgently needs feeding, you would like to be able to react quickly.

So you just use the key event corresponding to the game action FIRE for feeding the Javagochi:


??public void keyPressed (int keyCode) {
?? if (getGameAction (keyCode) == FIRE)
?? javagochi.transform (10);
??}

Now you can save the Javagochi from starvation using the FIRE game key.

Canvas and Text Input

As mentioned in the introduction to interaction, it is not possible to receive composed key events using the low-level API. But what can you do if you need this kind of input, such as for a text input trainer?

Let's just assume simple feeding is not enough for your Javagochi. Depending on its current state, it needs special vitamins, denoted by letters ranging from A to Z. On phones providing keys 0 through 9 only, this is a problem. The only solution is to emulate the key input mechanism in software. On cellular phones, there are also three to four letters printed on the number keys. In text input mode, pressing a number makes the first letter appear. If the same number is pressed again in a limited period of time, the second letter appears instead of the first one. This way you can cycle through all the letters on a number key. When no key is pressed for about three quarters of a second, or another key is pressed, the letter currently displayed is confirmed as input key. 

Continue...

(Page 1 of 2)  |1||2|Next

Was this article helpful to you?yesno

Related Publications
 
MIDP Programming (Part II)
MIDP Programming (Part I)

(Registered users can post questions/comments)

 
 TLINKS SEARCH
Advanced Search
Help
 Recommended Links
Red Cross
Responding to hurricane katrina relieve. Donate today. It's a Great Feeling to Help.
http://www.redcross.org
Getusjobs.com
Getusjobs.com is the job site focused on American jobs. See the results that put us on top.
http://www.getusjobs.com
Database Tool
TLinkSoft® tools empowers developers, integrators and DBAs to be more productive.
http://www.cppunit.org/download.jsp
USAnalyst.com
USAnalyst.com provide a community for database analysts, business analysts, developer analysts and managers.
http://www.cppunit.org/article

Powered by Tlinks Systems