MIDP user interface development

Develop rich mobile phone user interfaces using the MIDP UI

A core feature of the MIDP (Mobile Information Device Profile) technology is its support for developing mobile phone user interfaces. The MIDP provides a set of Java APIs known as the LCDUI, which has functionalities similar to the Java Abstract Windows Toolkit (AWT) and Swing APIs in the desktop world. This article covers mobile phone UI development using the MIDP APIs. Our focus is the MIDP 2.0 API, which is standard on all Nokia Developer Platform 2.0 devices. At the time of writing (late 2004), Developer Platform 1.0 devices, which are based on MIDP 1.0, still have a large installed base. Hence, the key differences between the MIDP 2.0 and MIDP 1.0 APIs are also briefly covered throughout the discussion. The topics in this article are as follows:

  • The design of the MIDP UI API: covers the overall design of the LCDUI API, including the distinction between the high-level and low-level APIs. We introduce the common display and UI event models for all LCDUI Screen and Canvas classes.
  • The low-level API: covers the pixel-based API. We discuss how to draw on the Canvas and how to handle keypad events. A useful example illustrates how to add animation functionality to Canvas applications.

The design of the MIDP UI API

Existing Java developers can get started with the MIDP very quickly because it leverages some proven design patterns in standard Java UI APIs, such as the AWT and Swing. The MIDP LCDUI supports two programming models: the widget-based, high-level model and the pixel-based, low-level model.

The UI models

The LCDUI high- and low-level APIs satisfy the needs of different developers. The high-level API is very easy to learn and to use. It aims to support fast development cycles, and it is easier to write portable code with the high-level API. The low-level API provides raw power and flexibility to developers who need to control every aspect of the user experience. In this section, we look at those two APIs from a bird's-eye view.

The high-level API

The design goal of the high-level API is to support programming applications that are portable across devices. It provides a set of standard UI classes representing basic UI components at a high abstraction level. Devices implement the components, and, therefore, an application programmed with high-level APIs adopts the native look and feel of that device. For example, the Series 40 and Series 60 devices often render the same widget differently according to their own UI guidelines. The high-level API is mostly suitable for business applications, which do not differentiate themselves with UI innovations. Such applications offer a "no-surprise" UI to users and have minimal learning curves. Their value comes from the content and functionalities behind the user interface.

However, the drawback with the high-level UI approach is also obvious: the developer has little control over the drawing details and cannot go beyond the predefined set of widget components. For instance, it would be hard to develop an animation screen using the high-level API alone.

The low-level API

The low-level API gives developers complete control of the entire device display, including drawing on any pixel, rendering basic shapes, and drawing text with specific fonts. The low-level API also supports richer user interactions than the high-level API, which only captures the soft-key events. The low-level API provides mechanisms for developers to handle all keypad key events and pointer movements. Custom rendering and event handling are crucial to game developers.

A low-level UI application needs to render itself rather than delegate the task to the runtime library. Hence, low-level API applications usually require much more code than the high-level ones. Porting low-level UI applications to different devices can sometimes be a tedious task. For instance, it is hard to implement a native look-and-feel text input box using pixel-level tools. In addition, a text input box implemented with a low-level API would not have the same performance as the ready-to-use high-level API component, since we now need to do all the font calculation and rendering on the Java level instead of the optimized native level. So, we should use the high-level components whenever possible and leave only the parts that require custom rendering to low-level APIs.

Architecture of the LCDUI

Figure 1 shows important classes and interfaces in the MIDP UI package. In this section, we focus on the API design of the four important classes: Display, Displayable, Command,, and CommandListener.

Figure 1. Important classes and interfaces of the MIDP UI. Click on thumbnail to view full-sized image.

Display

The Display class provides access to the physical screen. To avoid complications in multithread applications, only one instance of the Display object is allowed for each MIDlet. For this reason, the Display class constructor is private, and the single instance policy (the Singleton pattern) is enforced by a factory method. We can obtain the instance of the Display class by passing the MIDlet object to the static Display.getDisplay() method.

 public static Display getDisplay (MIDlet m) 

The most important methods in the Display class display Displayable objects to the physical screen. The Displayable object represents a view of the screen. In MIDP, there is only one Displayable object visible at any given time. It is different from the Windows programming model in which the forms display themselves and more than one window can be visible on the same screen. Hence, the setCurrent() method is used to switch the view.

 public void setCurrent(Displayable d)
public void setCurrent(Alert a, Displayable d)
public void setCurrentItem(Item item)
public Displayable getCurrent()

The first method simply displays a Displayable instance, such as a List or a Form to the screen. The second method pops up an Alert note. Upon dismissal, the specified Displayable object is shown. The third method displays the Displayable object that contains the specified UI component item (available on MIDP 2.0 devices only). The Display class makes sure that the item is properly focused and scrolled to be visible.

The setCurrent() method call is asynchronous. That means the screen change may not take place immediately upon the application calling setCurrent(). The screen is only updated and rendered when the UI thread is idle. For example, if we call setCurrent() within a method running in the UI thread (e.g., a UI event callback method), the screen is not updated until the method returns. The getCurrent() method is hence a useful means of finding out whether the change has actually occurred.

Displayable

The Displayable class is an abstract representation of a full screen of display content. Despite being an abstract class, the Displayable does not contain any abstract method. It is abstract because the Displayable class does not contain any rendering logic and hence cannot be used directly. The abstract modifier and nonpublic constructor of the Displayable class force developers to use its concrete subclasses in the LCDUI, which do contain the rendering logic. We can query whether a Displayable object is shown on the LCD by calling its isShown() method. The Displayable class allows us to query the size of the screen display area. The returned values do not necessarily indicate the physical size of the LCD. Rather, it is the available screen area for the MIDlet, which is always smaller than the physical LCD size.

 public int getWidth()
public int getHeight()
public boolean isShown() 

The Displayable class also provides methods to add and manipulate the title and ticker of the display area. Figure 2 shows the title and ticker on Series 40 and Series 60 devices.

 public void setTitle(String title)
public String getTitle()
public void setTicker (Ticker ticker)
public Ticker getTicker () 
Figure 2. The title and ticker on Series 40 and Series 60 devices. Click on thumbnail to view full-sized image.
Note
A ticker is an optional line of text string scrolling across the top of the display area.

The most important functionality of the Displayable class is to provide the basic infrastructure for user interactions through the Observer design pattern. A key interface in the infrastructure is CommandListener, which has only one method declared.

 public interface CommandListener {
   void commandAction(Command command, Displayable
displayable);
} 

The developer provides an implementation of the commandAction() method to specify what to do when a specific command is invoked by the user. Figure 3 illustrates this pattern.

Figure 3. The observer pattern in MIDP user interaction. Click on thumbnail to view full-sized image.

The Command and CommandListener classes work together with the Displayable as follows.

  1. We can associate Command objects with a Displayable screen.
  2. Each Command object is mapped to a visual element, such as a soft key, on a Displayable screen.
  3. When the user presses the soft key, a UI event is generated against the current Displayable object.
  4. The Displayable object calls the commandAction() method in the registered CommandListener object and passes in the Command object mapped to the soft key. The listener object changes the display to the next screen by calling Display.setCurrent() from within the commandAction() method.
Warning
The commandAction() must return immediately to avoid blocking the user interface.

Note
The Observer pattern is a behavior design pattern that describes how application components communicate with each other. Its purpose is to provide a way for a component to flexibly broadcast messages to interested receivers. In the MIDP UI model, the Command object is a broadcaster. It broadcasts UI event messages to CommandListener objects and invokes the appropriate callback methods. The CommandListener objects are registered with the container Displayable object that holds the Command objects.

The Command-related methods in the Displayable class are used in all the examples throughout this book. We can add a Command, remove a Command, or register a CommandListener in the Displayable object. The Command-related methods are as follows.

 public void addCommand(Command cmd)
public void removeCommand(Command cmd)
public void setCommandListener(CommandListener l) 

Command

When a Command object is added to a Displayable object, it is mapped to a soft key on the phone. If the Displayable object contains more than two commands, the menu label for one soft key automatically becomes "Options." If the user presses that key, the rest of the commands are displayed in a menu. Figure 4 shows how the Command objects are mapped.

Figure 4. The mapping of Command objects. Click on thumbnail to view full-sized image.

Notice that the Options menu automatically gets the native look and feel on different devices. For each command, the MIDP API provides a mechanism for developers to specify its functional nature and priority. Based on that information, the Java runtime decides how to map the Command object to soft keys or Options menus.

The Command class constructors are as follows.

 Command (String label, int cmdType, int priority)
Command (String shortLabel, String longLabel,
   int cmdType, int priority) 

Now, let's examine the second version of the constructor, which contains all the optional arguments. The first argument in the constructor is a short text label for the Command. It is displayed when the Command is mapped to a soft key. The second argument is a long text label for the Command. It is displayed when the Command is shown in an expanded menu. On Nokia devices, the long text label is displayed in the expanded options menu only when it can fit into one line (typically three or four words). If it needs to be truncated, the short text label is used in the Options menu. The third argument is the type of the Command. It determines the mapping of the Command to soft keys. For example, on Nokia devices, the EXIT type Commands always map to a single soft key on the rightmost soft key or appear in the Options menu. The MIDP specification defines the following Command types: SCREEN, BACK, OK, CANCEL, HELP, STOP, EXIT, and ITEM. The last argument is the priority level of the Command. Higher priority corresponds to a smaller value here. Given the same type, a high priority Command is displayed in a more accessible position.

A sample application

In this article, we use a sample application to display various demo screens and demonstrate the usage of the MIDP UI APIs. The entry MIDlet class DriverMidlet holds the Display object and provides two shared commands for each of the 14 demonstration screens in the application. The Next command invokes the DriverMidlet.next() method to instantiate and display the next demonstration screen; the Exit command exits the application. The source code of the DriverMidlet is listed below. Notice that we add the shared commands to each of the demo screens.

1 2 3 Page 1
Page 1 of 3