MIDP Programming with J2ME
December 26, 2002
Receiving Changes from Interactive UI Items
If you run the new version of the TeleTransfer MIDlet, you can
change the currency using the ChoiceGroup, but the TextField
labels for Dollar and Cent are not changed accordingly. You need a way to notify
the application if a selection is made in the currency ChoiceGroup.
Receiving changes of interactive high-level UI items in MIDP is based on a
listener model similar to AWT. Classes implementing the
ItemStateListener interface are able to receive notifications for the
following events:
The events are sent to the method itemStateChanged() of the
ItemStateListener, where the item that has changed is given as a
parameter. In order to actually receive these events, the
ItemStateChangeListener must be registered using the
setItemStateListener() method of the corresponding Form.
Now that you know about item state change events, you can add the desired
functionality to your TeleTransfer MIDlet. First, you need to add the
ItemStateListener interface to the class declaration:
public class TeleTransfer extends MIDlet
implements ItemStateListener {
You also need to implement a corresponding itemStateChanged()
method. Since the itemStateChanged() method is called for changes of
all Items in the Form, you need to check the item parameter
indicating the event source first. If the source of the event is the currency
ChoiceGroup, you set the labels of the amount and fraction
TextFields correspondingly:
public void itemStateChanged (Item item) {
if (item == currency) {
int index = currency.getSelectedIndex ();
switch (index) {
case 0: amountWhole.setLabel ("Dollar");
amountFraction.setLabel ("Cent");
break;
case 1: amountWhole.setLabel ("Euro");
amountFraction.setLabel ("Cent");
break;
case 2: amountWhole.setLabel ("Yen");
amountFraction.setLabel ("Sen");
}
}
}
Just adding the interface and implementing the corresponding methods is not
sufficient to enable the MIDlet to receive state changes. Additionally, you need
to register your ItemStateListener at the Form containing the
currency item. You do so by calling the setItemStateListener() method
in the TeleTransfer constructor:
public TeleTransfer () {
mainForm.append (senderAccount);
...
mainForm.append (currency);
mainForm.setItemStateListener (this);
}
Figure 3.5 shows the new version of the
TeleTransfer example, where the labels are changed depending on the
state of the currency ChoiceGroup.
Figure 3.5 The TeleTransfer
MIDlet extended to change the labels depending on the state of the currency
ChoiceGroup.
Using Commands for User Interaction
Now you can enter all the information required for a telegraphic transfer,
but you have no means to initiate the actual transfer.
In contrast to desktop computers, which have plenty of screen space for
displaying buttons or menus, a different approach is necessary for mobile
devices. Some devices provide so-called soft buttons, which are buttons
without fixed functionality that are assigned dynamically depending on the
application context. The number of soft buttons may vary if they are available.
Other mobile devices do not even have space for soft buttons, but provide
scrolling menus. MIDP needs to abstract from the concrete device and to provide
a mechanism that is suitable for all devices, independent of the availability
and number of soft buttons. Thus, the lcdui package does not provide
buttons or menus, but an abstraction called Command.
Commands can be added to all classes derived from the
Displayable class. These classes are Screen and its subclasses
such as Form, List, and TextBox for the high-level
API and Canvas for the low-level API.
No positioning or layout information is passed to the
Commandthe Displayable class itself is completely
responsible for arranging the visible components corresponding to
Commands on a concrete device. The only layout and display information
that can be assigned to a Command except from the command label is
semantic information. The semantic information consists of a type and a
priority. The priority allows the device to decide which commands are displayed
as soft buttons if the number of commands exceeds the number of soft buttons
available. For additional commands not displayed as soft buttons, a separate
menu is created automatically. The type information is an additional hint for
the device about how to display the command. For example, if the Exit command is
always assigned to the leftmost soft button in native applications of a certain
device type, the MIDP implementation is able to make the same assignment. Thus,
a consistent look and feel can be accomplished for a device.
The available command type constants are listed in Table 3.5.
Table 3.5 Command Type Constants
|
Constant
|
Value
|
|
Command.BACK
|
Used for navigation commands that are used to return the user to the previous
Screen.
|
|
Command.CANCEL
|
Needed to notify the screen that a negative answer occurred.
|
|
Command.EXIT
|
Used to specify a Command for exiting the application.
|
|
Command.HELP
|
Passed when the application requests a help screen.
|
|
Command.ITEM
|
A command type to tell the application that it is appended to an explicit
item on the screen.
|
|
Command.OK
|
Needed to notify the screen that a positive answer occurred.
|
|
Command.SCREEN
|
A type that specifies a screen-specific Command of the
application.
|
|
Command.STOP
|
Interrupts a procedure that is currently running.
|
The Command constructor takes the label, the command
type and the priority as input. The Command class provides
read() methods for all these fields, but it is not possible to change
the parameters after creation. Using the addCommand() method, commands
can be added to a Form or any other subclass of Displayable.
As in the case of receiving state changes of UI widgets, the MIDP uses a
listener model for detecting command actions. For this purpose, the
lcdui package contains the interface CommandListener. A
CommandListener can be registered to any Displayable class
using the setCommandListener method. After registration, the method
commandAction() of the Commandlistener is invoked whenever the
user issues a Command. In contrast to AWT, only one listener is allowed
for each Displayable class. The commandAction() callback
method provides the Displayable class where the command was issued and
the corresponding Command object as parameters.
With this information, you can extend your TeleTransfer application
with the desired Commands. But before going into actual command
implementation, you need to add some corresponding functionality. You'll
add three commands: a Send command, a Clear command, and an Exit command. For
Clear, you just add a method setting the content of the fields of your form to
empty strings:
public void clear () {
receiverName.setString ("");
receiverAccount.setString ("");
amountWhole.setString ("");
amountFraction.setString ("");
}
The Send command is a bit more difficult since you do not yet have the
background to really submit information over the network. (Network connections
will be handled in Chapter 6, "Networking: The Generic Connection
Framework.") So you just display the content to be transmitted in an alert
screen as a temporary replacement:
public void send () {
Alert alert = new Alert ("Send");
alert.setString ("transfer " + amountWhole.getString ()
+ "." + amountFraction.getString () + " "
+ amountWhole.getLabel ()
+ "\nto Acc#" + receiverAccount.getString ()
+ "\nof " + receiverName.getString ());
alert.setTimeout (2000);
display.setCurrent (alert);
clear ();
}
For leaving the application, the MIDlet already provides the
notifyDestroyed() method, so you do not need to add anything here.
Now that you have implemented the corresponding functionality, the next step
is to add the actual Command objects to your application class:
static final Command sendCommand = new Command ("Send", Command.SCREEN, 1);
static final Command clearCommand = new Command ("Clear", Command.SCREEN, 2);
static final Command exitCommand = new Command ("Exit", Command.EXIT, 2);
In order to enable the MIDlet to receive command actions, you need to
implement the CommandListener interface, and the corresponding
commandAction() method. Depending on the command received, you call
send(), clear(), or notifyDestroyed():
public class TeleTransfer extends MIDlet
implements ItemStateListener, CommandListener {
public void commandAction (Command c, Displayable d) {
if (c == exitCommand) {
notifyDestroyed();
}
else if (c == sendCommand) {
send ();
}
else if (c == clearCommand) {
clear ();
}
}
With these modifications, your TeleTransfer MIDlet is able to handle
the desired commands. You still need to add the Commands to the
Form, and register the TeleTransfer MIDlet as a
CommandListener in order to actually receive the commands:
public TeleTransfer () {
...
mainForm.addCommand (sendCommand);
mainForm.addCommand (clearCommand);
mainForm.addCommand (exitCommand);
mainForm.setCommandListener (this);
}
Figure 3.6 shows the Send Alert
of the new version of your TeleTransfer application.
Figure 3.6 The TeleTransfer
MIDlet showing an alert that displays the transfer information as a summary
before sending.
Further Item Classes: Gauge and
DateField
Now you have used all the Item subclasses except Gauge and
DateField. Both classes are specialized input elements, where the
Gauge may also make sense as a pure read-only information item.
The Gauge item visualizes an integer value by displaying a
horizontal bar. It is initialized with a label, a flag indicating whether it is
interactive, and a maximum and an initial value. If a Gauge is
interactive, the user is allowed to change the value using a device-dependent
input method. Changes to the gauge value will cause ItemEvents if a
corresponding listener is registered to the form.
The following code snippet shows the construction of a non-interactive
Gauge labeled Progress that is initialized with a value of 0 and a
maximum of 100:
Gauge gauge = new Gauge ("Progress", false, 0, 100);
If a Gauge is used to display progress of a process that takes a
longer amount of time, you should also add a corresponding Stop command to the
form to abort the progress.
The current value of the Gauge can be set using the method
setValue() and read using the method getValue(). Analogous
setMaxValue() and getMaxValue() methods let you access the
maximum value of the Gauge.
The DateField is a specialized widget for entering date and time
information in a simple way. It can be used to enter a date, a time, or both
types of information at once. The appearance of the DateField is
specified using three possible input mode constants in the constructor. Possible
DateField mode constants are listed in Table 3.6.
Table 3.6 DateField Mode Constants
|
Constant
|
Value
|
|
DATE
|
Passed if the DateField should be used for entering a date only.
|
|
DATE_TIME
|
Used for creating a DateField to enter both date and time
information.
|
|
TIME
|
Used to enter time information only.
|
The DateField has two constructors in which a label
and the mode can be specified. Using the second constructor, an additional
TimeZone can be passed. The following code snippet shows how a
DateField for entering the date of birth can be initialized:
DateField dateOfBirth = new DateField ("Date of birth:", DateField.DATE);
After you enter the date into the DateField, it can be accessed
using the getDate() method. The DateField offers some
additional methods for getting information about the input mode and methods for
setting the date and the input mode as well. The concrete usage of the
DateField is shown in Chapter 9 in the Blood Sugar Logger application.