In this section, we take a look at the main APIs
provided by the MIDP profile for GUI application development. Designing a
user interface for Java ME applications on mobile devices is quite a
challenging task, because MIDP's flexibility allows it to be used in
hundreds of different device models, with different form factors,
hardware, screen sizes and input methods. Such wide availability makes
Java ME applications attractive to a range of users (enterprises,
gamers, and casual users), all of whom need to be considered when
creating effective UIs for MIDlets.
There are many differences (see Table 1)
between the hardware and software environments in which Java originated
(desktop computers) and the ones found in mobile devices, such as
Symbian smartphones.
Table 1. Differences between Java environments
Environment | Windows, Linux and Mac OS Personal Computers | Typical Symbian smartphone |
---|
Screen | 1024 × 768 or bigger
Landscape orientation | 240 × 320
352 × 416
800 × 352
176 × 208
Landscape or portrait orientation |
Input method | QWERTY keyboard
Mouse | Numerical keypad
QWERTY keyboard
Touch screen (with stylus)
Touch screen (with fingers) |
Display colors | 16 bit and higher, usually 32 or 64 bit | 16 bit (top phones) |
Processing power and memory | 1 GHZ+/512 MB RAM + | Around 350 MHz |
These differences (and the
many others that exist) make it inappropriate to port Swing or AWT
toolkits directly to mobile devices. They would suffer from poor
performance (due to slower processors and smaller memory), usability
problems (there cannot be multiple windows on a mobile phone) and poor
input mechanism compatibility, as this varies greatly among devices.
1. LCDUI Model for User Interfaces
The LCDUI toolkit is a set of
features for the implementation of user interfaces especially for
MIDP-based devices. LCDUI is a generic set of UI components split into
two categories, high-level and low-level.
The high-level UI
components' main characteristics are a consistent API and leaving the
actual UI layout and appearance to be implemented by the device
environment itself. Developers can write their code against a single set
of classes and objects and trust they will appear consistently in all
devices implementing that set. This approach ensures that high-level UI
components are portable across devices, appearing on the screen in a
manner that is consistent with the device's form factor, screen size and
input methods. The downside is that little control is left in the
developers' hands, and some important things, such as fonts, colors and
component positioning, can only be hinted at by the developer – the
implementation is free to follow those hints or not.
The low-level set of UI components consists of the Canvas class, the GameCanvas subclass and the associated Graphics
class. They provide fine-grained, pixel-by-pixel control of layout,
colors, component placement, etc. The downside of using the low-level
set is that the developer must implement basic UI controls (dialogs,
text input fields, forms), since the Canvas is simply a blank canvas which can be drawn upon. The CustomItem class of the javax.microedition.lcdui
package can be thought of as belonging to the low-level UI set, as it
provides only basic drawing functionalities, allowing developers to
specify layout and positioning of controls very precisely. However, as
custom items are only used within Forms they are discussed in Section 2.3.2.
1.1. The Event Model
The javax.microedition.lcdui
package implements an event model that runs across both the high- and
low-level APIs. It handles such things as user interaction and calls to
redraw the display. The implementation is notified of such an event and
responds by making a corresponding call back to the MIDlet. There are
four types of UI event:
events that
represent abstract commands that are part of the high-level API; the
Back, Select, Exit, Cancel commands seen in Symbian OS devices generally
fit this category
low-level events that represent single key presses or releases or pointer events
calls to the paint() method of the Canvas class
calls to an object's run() method.
Callbacks are serialized
and never occur in parallel. More specifically, a new callback never
starts while another is running; this is true even when there is a
series of events to be processed. In this case, the callbacks are
processed as soon as possible after the last UI callback has returned.
The implementation also guarantees that a call to run(), requested by a call to callSerially(), is made after any pending repaint() requests have been satisfied. There is, however, one exception to this rule: when the Canvas.serviceRepaints() method is called by the MIDlet, it causes the Canvas.paint() method to be invoked by the implementation and then waits for it to complete. This occurs whenever the serviceRepaints() method is called, regardless of where the method was called from, even if that source was an event callback itself.
1.2. The Command Class
Abstract commands are used to
avoid having to implement concrete command buttons; semantic
representations are used instead. The commands are attached to
displayable objects, such as high-level List or Form objects or low-level Canvas objects. The addCommand() method attaches a command to the displayable object. The command specifies the label, type and priority. The CommandListener
interface then implements the actual semantics of the command. The
native style of the device may prioritize where certain commands appear
on the UI. For example, Exit is always placed above the right softkey on
Nokia devices. There are also some device-provided operations that help
contribute towards the operation of the high-level API. For example,
screen objects, such as List and ChoiceGroup, have built-in events that return user input to the application for processing.
2. LCDUI High-Level API: Screen Objects
Alert, List, TextBox, and Form objects are all derived from Screen, itself derived from Displayable. Screen
objects are high-level UI components that can be displayed. They
provide a complete user interface, of which the specific look and feel
is determined by the implementation. Only one Screen-derived object can be displayed at a time. Developers can control which Screen is displayed by using the setCurrent() method of the Display class.
This section describes the
high-level API classes in a succinct manner, rather than going into
every detail. To find a complete description of each featured class,
please check the MIDP documentation.
2.1. Alert Object
An Alert object
shows a message to the user, waits for a certain period and then
disappears, at which point the next displayable object is shown. An Alert
object is a way of informing the user of any errors or exceptional
conditions. It may be used by the developer to inform the user that:
an error or other condition has been reached
a user action has been completed successfully
an event for which the user has previously requested notification has been completed.
To emphasize these states to the user, the AlertType
can be set to convey the context or importance of the message. For each
use case described above, there's a relevant type, such as ALARM,
CONFIRMATION, ERROR and INFO. These are differentiated by titles at the
top of the alert screen, icons drawn on the alert, and the sound played
when it is displayed.
2.2. List Object
A List object is a
screen object that contains a list of choices for the user and is,
therefore, ideal for implementing choice-based menus, which are the core
user interface of most mobile devices.
2.3. TextBox Object
A TextBox is a Screen object that allows the user to enter and edit text in a separate space away from the form. It is a Displayable
object and can be displayed on the screen in its own right. Its maximum
size can be set at creation, but the number of characters displayed at
any one time is unrelated to this size and is determined by the device
itself.
2.4. Form Object
A Form object is
designed to contain a small number of closely-related user interface
elements. Those elements are, in general, subclasses of the Item class and we shall investigate them in more detail below. The Form object manages the traversal, scrolling and layout of the items.
Items enclosed within a form may be edited using the append(), delete(), insert() and set() methods. They are referred to by their indexes, starting at zero and ending with size()−1.
Items are organized via a layout policy that is based around rows. The
rows typically relate to the width of the screen and are constant
throughout. Forms grow vertically and a scroll bar is introduced as
required. If a form becomes too large, it may be better for the
developer to create another screen. Users can interact with a Form and a CommandListener can be attached to capture this input using the setCommandListener() method. An individual Item can be given an ItemCommandListener, if a more contextual approach is required by the UI.
2.5. Item Class
This is the superclass for all items that can be added to a Form. Every Item has a label, which is a string. This label is displayed by the implementation as near as possible to the Item, either on the same horizontal row or above. When an Item is created, by default it is not owned by any container and does not have a CommandItemCommandListener. However, default commands can be attached to an Item, using the setDefaultCommand()
method, which makes the user interface more intuitive for the user. A
user can then use a standard gesture, such as pressing a dedicated
selection key or tapping on the item with a pointer. Symbian devices
support these interfaces through S60 and UIQ, respectively. or
The following types are derived from Item.
2.5.1. ChoiceGroup
A group of selectable objects may be used to capture single or multiple choices in a Form. It is a subclass of Item and most of its methods are implemented via the Choice interface.
A ChoiceGroup has similar features to a List object, but it's meant to be placed in a Form, not used as a standalone Screen
object. Its type can be either EXCLUSIVE (to capture one option from
the group) or MULTIPLE (to capture many selections). As usual with
high-level UI components, developers don't have control over the
graphical representation of a ChoiceGroup.
Usually, though, one of EXCLUSIVE type is shown as a list of radio
buttons, while the MULTIPLE type is rendered as a list of checkboxes.
2.5.2. CustomItem
CustomItem operates in a similar way to Canvas:
the developer can specify precisely what content appears where within
its area. Some of the standard items may not give quite the required
functionality, so it may be better to define home-made ones instead. The
drawback to this approach is that, as well as having to draw all the
contents using the item's paint() method, the developer has to process and manage all events, such as user input, through keyPressed().
Custom items may interact with either keypad- or pointer-based devices.
Both are optional within the specification and the underlying
implementation will signal to the item which has been implemented.
CustomItem also inherits from Item, therefore inheriting the getMinContentWidth() and getPrefContentHeight() methods, which help the implementation to determine the best fit of items within the screen layout. If the CustomItem is too large for the screen dimensions, it resizes itself to within those preferred, minimum dimensions and the CustomItem is notified via sizeChanged() and paint() methods.
Additionally, the developer can use the Display.getColor(int) and Font.getFont(int) methods to determine the underlying properties for items already displayed in the form of which the CustomItem is a part, to ensure that a consistent appearance is maintained.
2.5.3. DateField
This is an editable component that may be placed upon a Form
to capture and display date and time (calendar) values. The item can be
added to the form with or without an initial value. If the value is not
set, a call to the getDate() method returns NULL. The field can handle DATE values, TIME values, and DATE TIME values.
2.5.4. ImageItem
The ImageItem is a reference to a mutable or immutable image that can be displayed on a Form. We look at the Image object in detail in Section 2.3.4. Suffice to say that the Image
is retrieved from the MIDlet suite's JAR file in order to be displayed
upon the form. This is performed by calling the following method, in
this case from the root directory:
Image image = Image.createImage("/myImage.png");
An ImageItem can be rendered in various modes, such as PLAIN, HYPERLINK, or BUTTON. Please check the MIDP documentation for more details.
2.5.5. Gauge
This component provides a
visual representation of an integer value, usually formatted as a bar
graph. Gauges are used either for specifying a value between zero and a
maximum value, or as a progress or activity monitor.
2.5.6. Spacer
This blank,
non-interactive item with a definable minimum size is used for
allocating flexible amounts of space between items on a form and gives
the developer much more control over the appearance of a form. The
minimum width and height for each spacer can be defined to provide space
between items within a row or between rows of items on the form.
2.5.7. StringItem
This is a display-only item that can contain a string and the user cannot edit the contents. Both the label and content of the StringItem can, however, be changed by the application. As with ImageItem,
its appearance can be specified at creation as one of PLAIN, HYPERLINK
or BUTTON. The developer is able to set the text, using the setText() method, and its appearance, using setFont().
2.5.8. TextField
A TextField is an editable text component that may be placed in a Form. It can be given an initial piece of text to display. It has a maximum size, set by setSize(int size),
and an input mask, which can be set when the item is constructed. An
input mask is used to ensure that end users enter the correct data,
which can reduce user frustration. The following masks can be used: ANY,
EMAILADDR, NUMERIC, PHONENUMBER, URL, and DECIMAL. These constraints
can be set using the setConstraints() method and retrieved using getConstraints(). The constraint settings should be used in conjunction with the following set of modifier flags using the bit-wise AND (&) operator: PASSWORD, SENSITIVE, UNEDITABLE, NON_PREDICTIVE, INITIAL_CAPS_WORD, INITIAL_CAPS_SENTENCE.
2.5.9. Ticker
This implements a ticker-tape
object – a piece of text that runs continuously across the display. The
direction and speed of the text is determined by the device. The ticker
scrolls continuously and there is no interface to stop and start it. The
implementation may pause it when there has been a period of inactivity
on the device, in which case the ticker resumes when the user
recommences interaction with the device.
3. LCDUI Interfaces
The user interface package, javax.microedition.lcdui, provides four interfaces that are available to both the high- and low-level APIs:
Choice defines an API for user-interface components, such as List and ChoiceGroup.
The contents of these components are represented by strings and images
which provide a defined number of choices for the user. The user's input
can be one or more choices and they are returned to the application
upon selection.
CommandListener
is used by applications that need to receive high-level events from the
implementation; the listener is attached to a displayable object within
the application using the addCommand() method.
ItemCommandListener is a listener type for receiving notification of commands that have been invoked on Item objects. This provides the mechanism for associating commands with specific Form items, thus contextualizing user input and actions according to the current active item on the form, making it more intuitive.
ItemStateListener
is used by applications that need to receive events that indicate
changes in the internal state of the interactive items within a form;
for example, a notification is sent to the application when the set of
selected values within a ChoiceGroup changes.
4. LCDUI Low-Level API: Canvas
The low-level API allows
developers to have total control of how the user interface looks and how
components are rendered on the screen. Canvas,
the main base class for low-level UI programming, is used to exercise
such fine-grained control. An application should subclass Canvas
to create a new displayable screen object. As it is displayable, it can
be used as the current display for an application just like the
high-level components. Therefore a MIDlet application can have a user
interface with, for example, List, Form and Canvas objects, which can be displayed one at a time to provide the application functionality to the users.
Canvas is commonly used by game developers when creating sprite animation and it also forms the basis of GameCanvas, which is part of the Game API . Canvas can be used in normal mode, which allows title and softkey labels to be displayed, and full-screen mode, where Canvas takes up as much of the display as the implementation allows. In either mode, the dimensions of the Canvas can be accessed using the getWidth() and getHeight() methods.
Graphics are drawn to the screen by implementing code in the abstract paint()
method. This method must be present in the subclass and is called as
part of the event model. The event model provides a series of user-input
methods such as keyPressed() and pointerPressed(), depending upon the device's data input implementation. The paint(Graphics g) method passes in a Graphics object, which is used to draw to the display.
The Graphics object
provides a simple 2D, geometric-rendering capability, which can be used
to draw strings, characters, images, shapes, etc. For more details,
please check the MIDP documentation.
Such methods as keyPressed() and pointerPressed() represent the interface methods for the CommandListener.
When a key is pressed, it returns a key code to the command listener.
These key codes are mapped to keys on the keypad. The key code values
are unique for each hardware key, unless keys are obvious synonyms for
one another. These codes are equal to the Unicode encoding for the
character representing the key. Examples of these are KEY_NUM0,
KEY_NUM1, KEY_STAR, and KEY_POUND. The problem with these key codes is
that they are not necessarily portable across devices: other keys may be
present on the keypad and may form a distinct list from those described
previously. It is therefore better, and more portable, to use game
actions instead. Each key code can be mapped to a game action using the getGameAction(int keyCode)
method. This translates the key code into constants such as LEFT,
RIGHT, FIRE, GAME_A and GAME_B. Codes can be translated back to key
codes by using getKeyCode(int gameAction).
Apart from making the application portable across devices, these game
actions are mapped in such a way as to suit gamers. For example, the
LEFT, RIGHT, UP and DOWN game actions might be mapped to the 4, 6, 2 and
8 keys on the keypad, making game-play instantly intuitive.
A simple Canvas class might look like this:
import javax.microedition.lcdui.*;
public class SimpleCanvas extends Canvas {
public void paint(Graphics g) {
// set color context to be black
g.setColor(255, 255, 255);
// draw a black filled rectangle
g.fillRect(0, 0, getWidth(), getHeight());
// set color context to be white
g.setColor(0, 0, 0);
// draw a string in the top left corner of the display
g.drawString("This is some white text", 0, 0, g.TOP | g.LEFT);
}
}