Chapter 4. MIDlet User Interfaces

MIDlets are intended to be portable to a range of devices with widely varying input and display capabilities, ranging from the very small, mainly two-color screens and restricted keypads on pagers and cell phones to the larger, often multicolor displays and more sophisticated keyboards or handwriting recognizers available on PDAs. Creating a set of user interface components suitable for such a wide range of devices is not a simple task. One option available to the MIDP designers was to use a subset of the Abstract Windows Toolkit (AWT) or Swing components from J2SE. However, this is not really a viable solution. Resource constraints rule out the adoption of Swing, while the basic user interface model around which the AWT is based is far too complex to be used on small devices.

Both AWT and Swing are based on giving the developer maximum freedom to create a rich and complex GUI in a multiwindowed environment, in which the user might be interacting with several applications at the same time. By contrast, because of the limited screen size, cell phone users do not expect to be able to work with more than one window or even more than one MIDlet at any given time. Instead of trying to find a subset of the AWT that would be appropriate for this restricted environment, the MIDP expert group chose to introduce a much simpler set of components and a lighter, screen-based programming model. In this model, the MIDlet developer focuses more on the business logic of the application rather than on the minute details of the user interface itself. The result is a class library that is much smaller and easier to use and also less demanding of memory and processor resources than either Swing or AWT.

The price to be paid for this simplicity is that developers using this “high-level” API are much less able to control the exact look and feel of their MIDlets; the programming interface does not include features that would allow customization of colors, fonts, or even component layout. The high-level API is covered in the second half of this chapter, but it does not represent the entire scope of the MIDlet user interface support. Recognizing that some application types, such as games (which are likely to form a large part of the software market for cell phones) require a much greater level of control, MIDP also includes a “low-level” user interface API. This API gives the developer exactly the opposite of the high-level API, namely complete control over (a part of) the screen and access to the keypad and whatever pointing device might be available. The trade-off for this greater control is greater responsibility: using the low-level API means writing code to draw everything that appears on the user’s screen and interpreting every input keystroke and pointer movement to decipher what the user wants to do. J2SE developers with experience creating custom components for AWT and Swing applications will probably feel very much at home with the low-level API, which is covered in the next chapter.

User Interface Overview

The user interface model for MIDP devices is very simple. J2SE applications often consist of several simultaneously visible windows between which the user can move the input focus simply by clicking with the mouse. A MIDP device, on the other hand, is required to display only a single “window” at a time, and the ability to move from one window to another depends on whether the MIDlet developer includes UI components that allow the user to do so. Furthermore, if there is more than one MIDlet running in a device at the same time, only one of them can have access to the screen, and the device may or may not provide a way for the user to select which MIDlet should be given the screen at any particular time. The MIDlet user interface library, which is implemented in the javax.microedition.lcdui package, includes several classes that represent the device’s screen and provide the basic top-level windows. Developers can use these as the basis for building form-based MIDlets or more graphically sophisticated MIDlets, such as games.

The Display and Displayable Classes

The Display class represents a logical device screen on which a MIDlet can display its user interface. Each MIDlet has access to a single instance of this class; you can obtain a reference to it by using the static getDisplay( ) method:

public static Display getDisplay(MIDlet midlet);

A MIDlet usually invokes getDisplay( ) when its startApp( ) method is called for the first time and then uses the returned Display object to display the first screen of its user interface. You can safely call this method at any time from the start of the initial invocation of the startApp( ) method, up to the time when the MIDlet returns from destroyApp( ) or notifyDestroyed( ), whichever happens first. Each MIDlet has its own, unique and dedicated instance of Display, so getDisplay( ) returns the same value every time it is called. A MIDlet will, therefore, usually save a reference to its Display object in an instance variable rather than repeatedly call getDisplay( ).

Every screen that a MIDlet needs to display is constructed by mounting user interface components (which are called items in MIDP terminology) or drawing shapes onto a top-level window derived from the abstract class Displayable , which will be discussed later. A Displayable is not visible to the user until it is associated with the MIDlet’s Display object using the Display’s setCurrent( ) method:

public void setCurrent(Displayable displayable)

Similarly, the Displayable currently associated with a Display can be retrieved by calling getCurrent( ) :

public Displayable getCurrent( )

Since a Display can show only one screen at a time, calling the setCurrent( ) method causes the previously displayed screen, if any, to be removed and replaced with the new one. However, the effect of calling setCurrent( ) is not guaranteed to be immediate; the device is allowed to defer the change to a more convenient time. This has the following consequences:

  • Code such as the following:

    Form newForm = new Form("New Form");
    display.setCurrent(newForm);
    Form currentForm = display.getCurrent( );
    System.out.println(newForm == currentForm);

    (where Form is a kind of Displayable that will be introduced shortly) does not necessarily print “true” because getCurrent( ) may return the Displayable that was installed before setCurrent( ) was called.

  • Installing a new Displayable and then blocking to perform a slow operation, such as making a network connection, is likely to result in the MIDlet appearing to stop with the previous screen on display. If you want to display a “Please wait...” message to make it clear to the user that a long-lasting operation is in progress, it is best to call setCurrent( ) to install a new Form containing the message and initiate the operation in a separate thread. The original thread can then continue unblocked and eventually display the message.

The Display object does not correspond directly to the device’s screen. Instead, it acts as a virtual screen that the MIDlet can use to control what it would like to display. If there is more than one active MIDlet, only one of them can control the real screen at any given time. The MIDlet that has direct access to the screen is said to be in the foreground, and other MIDlets are in the background. The MIDP device’s AMS is responsible for selecting which MIDlet is in the foreground at any given time. When a MIDlet is moved to the foreground, the Displayable selected in its Display object is switched into the screen, and the MIDlet’s startApp( ) method is called, as described in Section 3.4. Figure 4-1 shows the relationship between the device screen and the Display and current Displayable of foreground and background MIDlets.

Foreground MIDlet and the Display object

Figure 4-1. Foreground MIDlet and the Display object

Once a MIDlet has the foreground, it retains it until it does one of the following things:

  • Invokes its notifyPaused( ) method to request a temporary move to the background state

  • Invokes its notifyDestroyed( ) method to indicate that it no longer wants to be scheduled into the foreground

Although a MIDlet would normally call these methods as part of its event handling in response to user commands, a background thread running in the same MIDlet (or even in another MIDlet) may also invoke them to move the MIDlet out of the foreground.

Since the current Displayable is an attribute of the Display object, a background MIDlet also has a current Displayable, which it may change by calling the setCurrent( ) method if it has threads or timers running while it is not in the foreground. These changes have no effect on what the user sees until the MIDlet returns to the foreground.

A MIDlet can determine whether a given Displayable is visible to the user by calling isShown( ) , which is one of the four methods of the Displayable class:

public abstract class Displayable {
        public boolean isShown( );
        public void addCommand(Command cmd);
        public void removeCommand(Command cmd);
        public void setCommandListener(CommandListener l);
}

The isShown( ) method returns true only when the Displayable can actually be seen by the user, which requires that it be the current Displayable of the MIDlet’s Display and that the MIDlet be in the foreground. However, this condition is not sufficient, as the following code illustrates:

Form newForm = new Form("New Form");
display.setCurrent(newForm);
System.out.println("New form is shown? " + newForm.isShown( ));

In this case, newForm may not yet be visible, because the effect of setCurrent( ) is not required to be immediate.

The other three methods of the Displayable class deal with the addition and removal of Command objects and the registration of a listener to receive events from Commands. As the name suggests, Commands allow the user to request that an action be performed, such as opening a network connection, switching to another screen, or terminating the MIDlet. Commands are discussed in detail later in Section 4.2.4.

The High- and Low-Level User Interface APIs

Displayable is the base class for all MIDlet user interfaces, but it doesn’t provide enough functionality to be useful in its own right. There is a set of more useful classes, derived from Displayable, that can be used as the basis for building real user interfaces. The class hierarchy for these classes is shown in Figure 4-2.

Top-level user interface classes

Figure 4-2. Top-level user interface classes

As you can see, there are two direct subclasses of Displayable, both of which are also abstract. These two subclasses are the starting points for the two different styles of user interface programming supported by the javax.microedition.lcdui package.

Canvas

The Canvas class is the cornerstone of the low-level GUI API. Canvas acts like a blank sheet of paper that covers most of the user’s screen. In order to create a user interface using the low-level API, you subclass Canvas and implement the paint( ) method to draw directly on the screen. You can also respond to user input by overriding methods that are called as a result of key presses or pointer movements. The low-level API does not provide any individual components to handle text input, display lists, offer choices, and so on, although it does include the ability to use Commands, which Canvas inherits from Displayable. The low-level API is well suited for writing graphical games or displaying data in chart form and is described in detail in Chapter 5.

Screen

Screen is the basic class from which the top-level windows of the high-level API are derived. Like Canvas, Screen is an abstract class, but, unlike Canvas, developers are not expected to subclass it in order to implement a MIDlet user interface. Screen adds to Displayable the ability to include an optional title string and an optional ticker, which displays a continuously scrolling text message. The most commonly used concrete subclass of Screen is Form, which allows you to build a user interface by adding standard components (referred to as Items) to it, much like you add Components to a Container in the AWT. List, TextBox, and Alert, which is the MIDP equivalent of a dialog, are other subclasses of Screen. Unlike the low-level API, the high-level API does not allow the developer to draw directly to the screen or to handle events from the keyboard or the pointer. Instead, these events are handled internally and, where appropriate, are converted to higher-level events that originate from the Items that appear on the user’s screen.

Although the low- and high-level APIs are very different in style, they can be used together within a MIDlet. A typical example of this might be using the high-level API to create a form that allows the user to specify the location of some data, then switching to a Canvas on which the data is presented as a chart. You cannot, of course, use the high- and low-level APIs on the same screen.

Get J2ME in a Nutshell now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.