Sun Java Solaris Communities My SDN Account Join SDN
 
Article

MIDP Event Handling

 

Events

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.

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

Event Example Event Example Event Example
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.

State Listener State Listener State Listener State Listener
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:
  1. The keyRepeated method might not be available on all devices. Your application should check the availability of repeat actions by calling the hasRepeatEvents method.

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

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




Reader Feedback
Excellent   Good   Fair   Poor  

If you have other comments or ideas for future technical tips, please type them here:

Comments:
If you would like a reply to your comment, please submit your email address:
Note: We may not respond to all submitted comments.

Have a question about Java programming? Use Java Online Support.



Back To Top