Event Handling Through the Item Object
In the last article of this MIDP series, you learned about event processing with the Command object. Commands are mapped either to soft buttons on a device or to a menu system. When working with high-level user interface components, an additional means of event management is available through the Item object. This object is the focus of this final article in this series.
The Basics
An Item is any component that can be added to a Form, including ChoiceGroup, DateField, Gauge, ImageItem, StringItem, and TextField. With the exception of StringItem and ImageItem, each can detect user interaction.
When you add an Item to a Form, you create a "listener" to capture user events (for any or all items on the form). At some point after an event has been triggered, the method itemStateChanged()is called. Inside this method you can determine which Item was changed and how you want to proceed.
The specification does not require itemStateChanged() to be called upon each and every change. However, it does set forth some rules:
If an Item has changed, itemStateChanged() must be called for the changed Item before it will acknowledge changes in a subsequent Item.
If a MIDlet makes a change to an Item (compared to a user interaction), itemStateChanged()is not called. For example, if you write code inside your MIDlet to change the value of a DateField, this does not generate an event.
If the device running the MIDlet can recognize when a user has moved from one Item to another (changed focus), itemStateChanged() must be called when leaving one Item and before getting to the next.
Creating an Item
Item is an abstract class, so you do not create instances of the Item class. Instead, you create objects that have subclassed Item (ChoiceGroup, DateField, and so forth). The following code block creates a DateField, adds it to a Form, and sets a listener so that the Form can detect events:
private DateField dfDate; // Display the date // Create the datefield and populate with current date dfDate = new DateField("Date is:", DateField.DATE); dfDate.setDate(new java.util.Date()); // Create form and append the datefield fmMain = new Form("Core J2ME"); fmMain.append(dfDate); // Capture Item events fmMain.setItemStateListener(this);
Capturing Events on a DateField Object
In Listing 1, you will create a DateField and associate a listener with the main Form to capture events. When the DateField is shown on the display, you can modify the month, day, and year. When you exit the DateField component, the method itemStateChanged()is called. To reassure yourself that you actually enter this method, you will change the label on the DateField and print a message to the console.
Listing 1: DateFieldTest.java
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import java.util.*; public class DateFieldTest extends MIDlet implements ItemStateListener, CommandListener { private Display display; // Reference to Display object private Form fmMain; // The main Form private Command cmExit; // A Command to exit the MIDlet private DateField df; // Display the date public DateFieldTest() { display = Display.getDisplay(this); // Create calendar object Calendar c = Calendar.getInstance(); c.set(Calendar.YEAR, 2001); c.set(Calendar.MONTH, Calendar.NOVEMBER); c.set(Calendar.DAY_OF_MONTH, 19); // Create the datefield and populate with date df = new DateField("Date is:", DateField.DATE); df.setDate(c.getTime()); cmExit = new Command("Exit", Command.EXIT, 1); // Create the Form, add Command and DateField // listen for events fmMain = new Form("Core J2ME-DateField"); fmMain.addCommand(cmExit); fmMain.append(df); fmMain.setCommandListener(this); // Capture Command events fmMain.setItemStateListener(this); // Capture Item events } public void startApp() { display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c, Displayable s) { if (c == cmExit) { destroyApp(false); notifyDestroyed(); } } public void itemStateChanged(Item item) { System.out.println("Inside itemStateChanged()"); df.setLabel("New Date: "); } }
Figure 1 shows the DateField component with the requested date, as specified through the Calendar object. Once you choose the Options soft button, the leftmost screen shot in Figure 2 is displayed.
Figure 1 DateField component shown on a Form.
The middle screen shot of Figure 2 shows the DateField component in edit mode; the rightmost screen shot shows an updated day, month, and year.
Figure 2 Modifying a DateField.
Selecting the Ok soft button returns you to the original Form. Two clues in Figure 3 tell you that the method itemStateChanged() was called. First, the label on the DateField has changed to New Date. Second, there is a message written to the console, "Inside itemStateChanged()." The console output was captured from JBuilder running the Nokia MobileSet.
Figure 3 Updated DateField and message to console.
itemStateChanged() is not necessarily called for every change on an Item. For example, when the DateField component is shown on the display (what I referred to as edit mode), changing the day will not initiate an event. Instead, the method itemStateChanged() is called when the user exits the DateField.
Capturing Events on a ChoiceGroup Object
Let's contrast the DateField interaction with that of a ChoiceGroup component. Listing 2 creates a ChoiceGroup with three entries (see Figure 4).
Listing 2: ChoiceGroupTest.java
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ChoiceGroupTest extends MIDlet implements CommandListener, ItemStateListener { private Display display; // Reference to display object private Form fmMain; // The main form private Command cmExit; // Exit the form private ChoiceGroup cg; // ChoiceGroup public ChoiceGroupTest() { display = Display.getDisplay(this); // Create the choicegroup cg = new ChoiceGroup("Email Options", Choice.EXCLUSIVE); cg.append("Reply", null); cg.append("Forward", null); cg.append("Delete", null); // Create form, add objects, listen for events cmExit = new Command("Exit", Command.EXIT, 1); fmMain = new Form("Core J2ME-ChoiceGroup"); fmMain.addCommand(cmExit); fmMain.append(cg); fmMain.setCommandListener(this); fmMain.setItemStateListener(this); } public void startApp() { display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c, Displayable s) { if (c == cmExit) { destroyApp(false); notifyDestroyed(); } } public void itemStateChanged(Item item) { System.out.println("Inside itemStateChanged()"); } }
Figure 4 ChoiceGroup component.
The leftmost screen shot of Figure 5 shows the Delete entry before being selected. That is, we have simply scrolled down to the entry. To select this entry, press the soft button Options and choose Select, as shown on the right screen shot of Figure 5.
Figure 5 Selecting a new ChoiceGroup entry.
The Delete entry will now be marked as selected (see Figure 6). Also notice that a message has been written to the console, indicating that the method itemStateChanged() was called. ChoiceGroup will call itemStateChanged() whenever the component changes, whereas the DateField called that same method just once, when exiting the component.
Figure 6 ChoiceGroup with Delete entry selected.
To bring this point home, let's select a different entry in ChoiceGroup. Figure 7 shows the device display and the console output when moving up one entry and choosing Forward. Notice that an additional message is written to the console, indicating another call to itemStateChanged().
Figure 7 ChoiceGroup with Forward entry selected.