by Qusay H. Mahmoud
Source: EventEx1.java, EventEx2.java, EventEx3.java
When a user interacts with a MIDlet loaded on a mobile information device, some
events are generated. For example, if the user selects Save from the File menu, the
application is notified of this action and responds to the generated event. But how
does the application get notified of an event, and how does it handle it? As
mentioned in the MIDP GUI
Programming: Programming the Phone Interface, there
are two MIDP user interface APIs: high-level and low-level. Therefore, there are
two kinds of events: high-level (such as selecting an item from a list) and
low-level (such as pressing a key on the device).
This article uses examples to discuss how to handle high-level and low-level MIDP
events. The discussion begins with an explanation of callbacks.
Callbacks Defined
When a user interacts with a MIDlet, events are generated and the application is
modified to handle and respond to these events. The application is notified of such
events through callbacks. Callbacks are invocation of programmer-defined
methods that are executed by the application in response to actions taken by a user
at run time.
Callbacks are used in many programming environments, especially in GUI construction
kits. For example, the Abstract Window Toolkit (AWT) API makes heavy use of
callbacks. When a user interacts with a component, for example, the interface code
calls back the computational code to respond to the user's action. Thus, when a
user selects an item from a pull-down menu, the interface code for the menu will
call back the computational code to invoke the selected item from the menu.
In some languages such as C/C++, callbacks are implemented by passing a function
pointer to another function. The receiving function uses the function pointer to
invoke another function when a particular event occurs. Because the Java
programming language does not have pointers, callbacks are implemented with
interfaces. An interface defines a set of methods, but unlike a class does not
implement their behavior. Instead, you provide the interface method implementations
for the class that implements the interface.
There are four kinds of user interface callbacks in MIDP:
- Abstract commands that are part of the high-level API.
- Low-level events that represent single key presses and releases.
- Calls to the
paint() method of a Canvas class.
- Calls to a
Runnable object's run method requested
by a call to the callSerially method of the Display
class.
 |
 |
 |
 |
 |
Note: All user interface callbacks are serialized. This means they can never
occur in parallel. User interface callbacks are called as soon as the previous
callback returns. In addition, the MIDP user interface API is thread-safe and
includes a mechanism for event synchronization. An application can use the
callSerially method of the
Display class to execute an operation serially with events.
|
 |
 |
 |
Screen Navigation
As a MIDlet developer, you are responsible for providing a way for the user to
navigate the different screens that make up your MIDlet. The
javax.microedition.lcdui package provides the Command
class, which you can read about in previous articles.
The Command class encapsulates the semantic information of an action.
The command itself contains only information about a command, but not the actual
action that happens when a command is activated. The action is defined in a
CommandListener object associated with the screen. Here is an example
of a Command object:
Command infoCommand = new Command("Info", Command.SCREEN, 2);
|
As you can see, the Command class constructor takes three parameters,
and therefore, contains the following three pieces of information: label,
type, and priority.
- The label is a string used for the visual representation of the command.
For example, the label may appear next to a soft button on the device or as an
element in a menu as shown in Figure 1.
- The type specifies the command's intent. The defined types are:
BACK, CANCEL, EXIT, HELP,
ITEM, OK, SCREEN, and STOP.
- The priority value describes the importance of this command relative to
other commands on the screen. A priority value of 1 indicates the most important
command, and higher priority values indicate commands of lesser importance.
When the MIDlet executes, the device chooses the placement of a command based on
the command type, and places similar commands based on their priority values.
Consider the following example with three commands:
exitCommand = new Command("Exit", Command.SCREEN, 1);
infoCommand = new Command("Info",Command.SCREEN, 2);
buyCommand = new Command("Buy", Command.SCREEN, 2);
|
In this example, the application manager maps the Exit command to the screen, and,
as shown in Figure 1, creates a Menu command to hold the Info and
Buy commands. Clicking the right soft button, under Menu, takes you to
a screen with a menu that with two buttons: info and buy.

Figure 1: Exit, Info, and Buy commands
The Command class provides the following three methods for retrieving
the type, label, and priority values. This information is helpful when handling
events.
int getCommandType()
String getLable()
int getPriority()
|
 |
 |
 |
 |
 |
Note: The MIDP UI API lets you set up a screen with no commands, but this is
generally not useful because the user can not move to another screen.
|
 |
 |
 |
Handling High-Level Events
Handling events in the high-level API is based on a listener model.
Screen and Canvas objects can have listeners for
commands. For an object to be a listener, it must implement the
CommandListener interface.
You add commands to a displayable object with the addCommand method,
and register a listener with the setCommandListener method. Both of
these methods are part of the Displayable class and they are inherited
by Screen and Canvas.
The CommandListener Interface
The CommandListener interface is for MIDlets that need to receive
high-level events from the implementation. This interface has one method that a
listener must implement, which is the commandAction method.
public void commandAction(Command c, Displayable d)
|
The c parameter is a command object that identifies the command (if
any) that has been added to Displayable with the
addCommand method. The d parameter is the displayable
where the event occurred.
Example: Handling High-Level Events
In this example, a list of items (Item1 to Item4) is created. The
prepare() method is called whenever an item is selected. The
testItem# methods, where # is a number between 1 and 4, call the
prepare method and set the name of the menu.
Listing 1: EventEx1.java
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class EventEx1 extends MIDlet
implements CommandListener {
// display manager
Display display = null;
// a menu with items
List menu = null; // main menu
// textbox
TextBox input = null;
// command
static final Command backCommand =
new Command("Back", Command.BACK, 0);
static final Command mainMenuCommand =
new Command("Main", Command.SCREEN, 1);
static final Command exitCommand =
new Command("Exit", Command.STOP, 2);
String currentMenu = null;
// constructor.
public EventEx1() {
}
/**
* Start the MIDlet by creating a list of items
* and associating the exit command with the list.
*/
public void startApp()
throws MIDletStateChangeException {
display = Display.getDisplay(this);
menu = new List("Menu Items", Choice.IMPLICIT);
menu.append("Item1", null);
menu.append("Item2", null);
menu.append("Item3", null);
menu.append("Item4", null);
menu.addCommand(exitCommand);
menu.setCommandListener(this);
mainMenu();
}
public void pauseApp() {
display = null;
menu = null;
input = null;
}
public void destroyApp(boolean unconditional) {
notifyDestroyed();
}
// main menu
void mainMenu() {
display.setCurrent(menu);
currentMenu = "Main";
}
/**
* a generic method that is called when any of
* the items on the list are selected.
*/
public void prepare() {
input = new TextBox("Enter some text: ",
"", 5, TextField.ANY);
input.addCommand(backCommand);
input.setCommandListener(this);
input.setString("");
display.setCurrent(input);
}
/**
* Test item1.
*/
public void testItem1() {
prepare();
currentMenu = "item1";
}
/**
* Test item2.
*/
public void testItem2() {
prepare();
currentMenu = "item2";
}
/**
* Test item3.
*/
public void testItem3() {
prepare();
currentMenu = "item3";
}
/**
* Test item4.
*/
public void testItem4() {
prepare();
currentMenu = "item4";
}
/**
* Handle events.
*/
public void commandAction(Command c, Displayable d) {
String label = c.getLabel();
if (label.equals("Exit")) {
destroyApp(true);
} else if (label.equals("Back")) {
if(currentMenu.equals("item1") ||
currentMenu.equals("item2") ||
currentMenu.equals("item3") ||
currentMenu.equals("item4")) {
// go back to menu
mainMenu();
}
} else {
List down = (List)display.getCurrent();
switch(down.getSelectedIndex()) {
case 0: testItem1();break;
case 1: testItem2();break;
case 2: testItem3();break;
case 3: testItem4();break;
}
}
}
}
|
As you can see, the EventEx1 class implements the
CommandListener interface by providing an implementation for the
commandAction method. In this implementation, the label of the command
is checked. If the label equals Exit, the MIDlet is destroyed, and if
the label equals Back and the current menu is item1 to
item4 the program goes back to the Main menu. Otherwise, the selected item
is found and the appropriate method is called. Note that when you have an item
lists, you use the Display.getCurrent method to return the list, and
then switch between the items to determine which item is selected.
If you run the EventEx1 MIDlet you see output similar to
Figure 2.
Figure 2: Handling High-Level Events
The ItemStateListener Interface
Applications use the ItemStateListener interface to receive events
that indicate changes in the internal state of items within a Form
screen. This happens when the user does any of the following:
- Adjusts the value of an interactive
Gauge.
- Enters or modifies the value of a
TextField.
- Enters a new date or time in a
DateField.
- Changes the set of selected values in a
ChoiceGroup.
This interface has only one method that a listener must implement:
public void itemStateChanged(Item item)
|
Use the setItemStateListener method to register a listener.
Example: Changing the Date
In this example a DateField object is created and added to a form.
When you click on the date, you can change it by navigating through the calendar.
When the date is changed, a message appears.
Listing 2: EventEx2.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class EventEx2 extends MIDlet {
Display display;
public EventEx2() {
display = Display.getDisplay(this);
}
public void destroyApp (boolean unconditional) {
notifyDestroyed();
System.out.println("App destroyed ");
}
public void pauseApp () {
display = null;
System.out.println("App paused.");
}
public void startApp () {
Form form = new Form("Change Date");
// an anonymous class
ItemStateListener listener =
new ItemStateListener() {
java.util.Calendar cal =
java.util.Calendar.getInstance(
java.util.TimeZone.getDefault());
public void itemStateChanged(Item item) {
cal.setTime(((DateField)item).getDate());
System.out.println("\nDate has changed");
}
};
// register for events
form.setItemStateListener(listener);
// get today's date
java.util.Date now = new java.util.Date();
DateField dateItem = new DateField(
"Today's date:", DateField.DATE);
dateItem.setDate(now);
// add date to the Form screen
form.append(dateItem);
display.setCurrent(form);
}
}
|
The ItemStateListener interface is implemented as an anonymous inner
class in Listing 2 by providing an implementation to the
itemStateChanged method.
If you run the EventEx2 MIDlet, you see output similar to Figure 3.
Figure 3: Implementing the ItemStateListener
Interface
Handling Low-Level Events
If you use the Canvas class to write applications to access low-level
input events or to issue graphics calls for drawing to the display, you must handle
low-level events. Game applications are likely to use the Canvas class
because it provides methods to handle game actions and key events. The key events
are reported with respect to key codes that are directly bound to concrete keys on
the device.
The Canvas class, which is a subclass of Displayable,
allows the application to register a listener for commands, but it requires
applications to subclass it first. Also, unlike screens that allow the application
to define listeners and register them with instances of the Screen
class, the Canvas class does not allow this because several new
listener interfaces need to be created with one for each kind of event.
Key Events
Every key for which events are reported is assigned a key code. The MIDP defines
the following key codes in the Canvas class:
KEY_NUM0
KEY_NUM1
KEY_NUM2
KEY_NUM3
KEY_NUM4
KEY_NUM5
KEY_NUM6
KEY_NUM7
KEY_NUM8
KEY_NUM9
KEY_STAR
KEY_POUND
Basically, these are the keys 0..9, *, and #. Other keys might exist on some
devices, but for portability, applications should use only the standard key codes.
Game Actions
If your application needs arrow key and gaming-related events, use game actions
instead of key codes. MIDP defines the following game actions:
DOWN
LEFT
RIGHT
FIRE
GAME_A
GAME_B
GAME_C
GAME_D
While each key code is mapped to one game action, a game action can be associated
with more than one key code. The translation between the two is done with the
getKeyCode and getGameAction methods.
 |
 |
 |
 |
 |
Note: If your application uses game actions and you want it to be portable,
you should translate key events into game actions with the
getGameAction method and test the result. For example, the game
actions UP, DOWN, LEFT, and
RIGHT can be mapped differently on different devices. The
getGameAction method would return the RIGHT game action,
for example, when the user presses the key that is a natural right on the
device.
|
 |
 |
 |
Event Delivery Methods
The following methods are available for handling low-level events.
protected void keyPressed(int keyCode)
protected void keyReleased(int keyCode)
protected void keyRepeated(int keyCode)
protected void pointerPressed(int x, int y)
protected void pointerDragged(int x, int y)
protected void pointerReleased(int x, int y)
protected void showNotify()
protected void hideNotify()
protected abstract void paint(Graphics g)
commandAction() method of the CommandListener interface
 |
 |
 |
 |
 |
Note:
- The
keyRepeated method might not be available on all devices.
Your application should check the availability of repeat actions by calling the
hasRepeatEvents method.
- The pointer events may not be present on all devices, so before using the
pointerPressed, pointerDragged, and
pointerReleased methods, your application should check if a pointer
mechanism is available by calling the hasPointerEvents and
hasPointerMotionEvents methods first.
|
 |
 |
 |
Example: Handling Low-Level Events
In this example, the Canvas class is subclassed to use an anonymous
inner class, an implementation is provided for the keyPressed and
keyReleased() methods, and an empty implementation is provided for the
paint method. When a key is pressed or released, the application
prints the value of that key.
Listing 3: EventEx3.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class EventEx3 extends MIDlet {
Display display;
Command exit;
public EventEx3() {
display = Display.getDisplay(this);
}
public void destroyApp (boolean unconditional) {
}
public void pauseApp () {
System.out.println("App paused.");
}
public void startApp () {
display = Display.getDisplay(this);
// anonymous class
Canvas canvas = new Canvas() {
public void paint(Graphics g) { }
protected void keyPressed(int keyCode) {
if (keyCode > 0) {
System.out.println("keyPressed "
+ ((char)keyCode));
} else {
System.out.println("keyPressed action "
+ getGameAction(keyCode));
}
}
protected void keyReleased(int keyCode) {
if (keyCode > 0) {
System.out.println("keyReleased "
+ ((char)keyCode));
} else {
System.out.println("keyReleased action "
+ getGameAction(keyCode));
}
}
}; // end of anonymous class
exit = new Command("Exit", Command.STOP, 1);
canvas.addCommand(exit);
canvas.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if(c == exit) {
notifyDestroyed();
} else {
System.out.println("Saw the command: "+c);
}
}
});
display.setCurrent(canvas);
}
}
|
If you run the EventEx3 MIDlet, you see output similar to Figure 4.
Figure 4: Handling Low-Level Events
Another possible implementation for the keyPressed method, for
example, is to interpret the keys at run time:
public void keyPressed(int keyCode) {
int action = getGameAction(keyCode);
switch(action) {
case LEFT: System.out.println("MOVE TO THE LEFT");break;
case RIGHT: System.out.println("MOVE TO THE RIGHT");break;
// and so on....
}
}
|
Conclusion
From these examples you can see how easy it is to handle events in MIDP. It is much
easier than handling events in AWT or Project Swing because as there are only a few
classes and interfaces to use, extend, or implement.
Because all the wireless handheld device manufacturers are interested in MIDP, not
all MIDP devices have the same features, and writing a portable MIDlet can be
challenging. But if you do not use the device-dependent APIs or assume that a
particular device has some specific keys, your MIDlet is much more platform
independent.
About the Author:
Qusay H. Mahmoud provides Java consulting and training services. He has published dozens of articles on Java, and is the author of Distributed Programming with Java (Manning Publications, 1999) and Learning Wireless Java (O'Reilly & Associates, 2002).
Back To Top
|
|