The Java 2 Micro Edition (J2ME), coupled with the CLDC and MIDP specifications, provides a platform that enables developers to build Java applications that run on PDAs and BlackBerry-style e-mail pagers and that interface with corporate software "back home" via a wireless connection. This article kicks off a series that will introduce you to development on the MIDP platform. We'll begin with the basics of J2ME GUI programming (the javax.microedition.lcdui classes) and move on to data storage and wireless connectivity. Throughout this series, I'll refer you to a sample Expenses MIDlet suite, which is designed to help you keep track of business expenses while you travel. Expenses will start out simple and grow with each new article.
What's all this CLDC and MIDP gobbledygook?
If you aren't sure what all the acronyms I'm throwing out mean, you may want to check out "Making sense of the J2ME alphabet soup." You might also want to refer to "Getting wireless with J2ME MIDP development" and "Building and testing J2ME MIDP applications with the Wireless Toolkit" for background information, a look at how to get the software you need, and an introduction to Sun's Wireless Toolkit, which I'll be using throughout this series to build and test the Expenses MIDlets.
As I said, we're starting out pretty simple. At this point, the Expenses application consists of a single MIDlet (Expenses) that provides a simple user interface and a data class (ExpenseInfo) that models an expense entry with fields for date and time, description, amount, and category. You can download the project source code here.
ExpenseInfo is fairly self-explanatory. I won't spend much time talking about it at this point; instead, I'll devote more time to the Expenses UI. I will, however, mention that MIDP does not include support for floating-point variables. As a result, I had to fake it by using two separate int fields for the dollars and cents portions of each expense amount.
Building the basic UI
Let's start by talking about the UI components Expenses makesuse of and then move on to event handling. In Figure A, you can see what Expenses lookslike at runtime in the DefaultGrayPhone emulator. The UI consists of a single List component and three Command components, one each for quitting the app, editing an expense item, and adding a new item. The data you see displayed is simply test data that's generated by a static method of the ExpenseInfo class. Since we aren't hooked up to a data store (yet), there's no way to preserve data across application runs.
|Running the Expenses MIDlet…pretty simple, huh?|
Working with a List
As you probably know (and if you don't, I suggest you check out the articles I mentioned earlier), there are three main types of lcdui components: those that descend from Screen, those that descend from Item, and miscellaneous components that don't really fit anywhere else. The primary distinction between Screen and Item components is that Screen components can act as a container for other components. The component making up the UI of Expenses is a List component named lsMain.
Lists are roughly analogous to the list boxes you'll find in almost any desktop application, with the main difference being that a List is its own window, while list boxes typically are contained in another window. Lists, being Screen objects, can contain other components, but Lists may contain only Command components.
There are three kinds of Lists, and you specify which kind you'd like when calling the List constructor by specifying a static constant of the list class. Here's a brief rundown of the three kinds of Lists:
- · Implicit Lists (as in Figure A) allow only one item to be selected and fire a commandAction event when an item is selected.
- · Exclusive Lists display their contents as radio buttons and allow only one item to be selected at a time.
- · Multiple Lists display their contents as check boxes and allow multiple items to be selected.
I've put the Expenses MIDlet'sconstructor in Listing A. As you can see, lsMain is created as an implicit List; several expense items are added to it using the append method; our three Commands are added with addCommand; and finally, the command listener for lsMain is set (setCommandListener) so that Expenses can handle events raised from lsMain.
"By your command…"
Now let's take a quick look at the Command object. Commands are miscellaneous controls that are roughly analogous to command buttons in desktop applications. The main difference is that, depending on the platform, a Command button may or may not have an onscreen representation. In cases where there is no room onscreen for a command button, or on devices using keypad navigation rather than pointer navigation, Commands may be mapped to appropriate buttons on the device or to multifunction buttons that can serve multiple purposes, also known as "soft buttons." You suggest an appropriate button when calling the Command constructor by specifying one of several static constants defined in the Command class.
As an example, look at the constructor call for cmExit:
cmExit = new Command("Exit", Command.EXIT, 1);
You can see that the second parameter passed to the constructor is Command.EXIT, which tells the application manager that cmExit should be mapped to a keyboard Exit button, should one be available. Figure B lists the command types you can specify.
|Possible command types|
You may have noticed in Figure A that the DefaultGrayPhone emulator doesn't have an Exit button. So, then what? Precisely what happens depends a bit on the implementation, but usually the Command's text will be displayed somewhere proximate to a soft button. You can see from Figure A that in this case, the Exit command was mapped to the left-hand soft button. If DefaultGrayPhone had an Exit button, the Exit command would probably not appear onscreen but would instead be mapped to the Exit button on the device.
Phantom Menu commands
Looking at Figure A, you may be wondering what happened to the cmAdd and cmEditCommands from Listing A and why there's a Menu command displayed over the right-hand soft button, since Expenses didn'tcreate one. Because I specified more commands for the app than there were soft buttons, the application manager created a Menu command for accessing the extra commands. Figure C shows the result of invoking the Menu command.
|Menu command results|
How do you know which Commands will have a dedicated button and which ones will live on a menu? Well, quite simply, you don't. The placement of commands is entirely left up to the platform implementer. You can, however, suggest that a Command be given a dedicated button by specifying a priority as the last argument to the Command constructor. The lower the number you specify, the harder the application manager will try to find a dedicated button for the Command. Since cmExit was given a priority of 1, it got the prime real estate, while cmEdit and cmAdd, with their priorities of 2 and 3, were relegated to a menu.
The event model for lcdui components is similar to the system used by AWT and Swing. Each type of event has an interface defined to represent that event, and each component will call a given method on that interface to signal the occurrence of that event to one or more listener objects. As you'd expect, the difference is that things are greatly simplified in lcdui's system. There are only a few events to work with, and each component can have only one listener registered at a time—hence the change from addXListener in AWT components to setXListener in lcdui classes.
There are two kinds of events that Expenses will beconcerned with: commandAction and itemStateChange. The commandAction event occurs when a user invokes a Command or selects an item in a List. On the other hand, itemStateChange is signaled when a user makes a change to the data displayed by an Item component. Since Expenses doesn't have any components that can raise itemStateChange events (yet), we can skip that for now and concentrate on commandAction.
A command performance
Classes that implement the CommandListener interface will receive event notifications from the components they register themselves with (by calling setCommandListener) through the interface's commandAction method. I've put the code for Expenses'commandAction method in Listing B. You can see that the event handling for the app appears pretty simple. The method is handed a variable instance of the object that signaled the event, and I examine that variable to determine what action to take.
An events listing
The lsMain component, being an implicit list, can also generate a commandAction event. Determining whether an event originated from a List component is accomplished by comparing the Command instance passed into commandAction to the static constant List.SELECT_COMMAND. In the case of Expenses,the lsMain command action will be equivalent to the cmEdit command action: Both will trigger editing of the selected expense item.
That's going to do it for now. Be sure to come back next time, when I'll show you how I built the detail form, which provides add/edit functionality and makes use of several Item components.