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.
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
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” becausegetCurrent( )
may return theDisplayable
that was installed beforesetCurrent( )
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 callsetCurrent( )
to install a newForm
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.
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 stateInvokes 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
Command
s. As the name suggests,
Command
s allow the user to request that an action
be performed, such as opening a network connection, switching to
another screen, or terminating the MIDlet.
Command
s are discussed in detail later in Section 4.2.4.
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.
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 subclassCanvas
and implement thepaint( )
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 useCommand
s, whichCanvas
inherits fromDisplayable
. 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. LikeCanvas
,Screen
is an abstract class, but, unlikeCanvas
, developers are not expected to subclass it in order to implement a MIDlet user interface.Screen
adds toDisplayable
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 ofScreen
isForm
, which allows you to build a user interface by adding standard components (referred to asItem
s) to it, much like you addComponent
s to aContainer
in the AWT.List
,TextBox
, andAlert
, which is the MIDP equivalent of a dialog, are other subclasses ofScreen
. 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 theItems
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.