The J2ME MIDP (Mobile Information Device Profile) 2.0 has been out of the box since November 2002. In terms of scope and vision, the new profile is an improvement over the original. Whereas the original MIDP specification was seemingly designed to meet the needs of a limited device group, the new profile has a much better grip on the potential of handset technology. New gaming and audio APIs, extended security and networking capabilities, and OTA (over the air) provisioning make MIDP 2.0 a full-service wireless development profile. And to top it all off, the expanded user interface API will give your applications a better look-and-feel and expanded usability.
The new user interface API includes a class specifically intended
for building unique or custom UI elements. CustomItem, like its parent class, Item, offers a simple framework for developing individual GUI forms. Unlike its parent class, CustomItem's base functionality can be extended easily and effectively, allowing for all kinds of variation on the standard Item template.
In this article I'll take the new MIDP 2.0 user interface API for a test drive. By developing a simple, custom GUI component (or Item), you will learn a little bit about the CustomItem class, as well as some of the other new features of MIDP 2.0.
MIDP 2.0 brings many exciting new features to the MIDP UI API. I can't address all of them here, so I'll just go over some of the biggest changes, as well as introducing the new features I'll actually work with in the programming exercise that follows.
- Display size: MIDP 2.0 introduces the concept of
size to the
ItemandCustomItemclasses. For every item, custom or standard, you can now set both the minimum display size and the preferred display size. An item's minimum size is the smallest size at which it can display and function properly. Its preferred size will yield minimum text wrapping and optimum display results. Whereas components developed with theItemclass can take standard display settings, theminimumandpreferredsettings have to be established manually forCustomItems. - Interaction modes:
CustomItems can take both keypad and pointer input. Modes of interaction areKEY_PRESS,KEY_RELEASE,KEY_REPEAT,POINTER_DRAG,POINTER_PRESS, andPOINTER_RELEASE. For every interaction, the application must find out which of the modes is supported by calling thegetInteractionModes()method, which returns a bitmask of the available interaction modes. - User input: A subclass of
CustomItemmakes it easy to create interactive components such as spreadsheets or calendars. The class allows users to revise or update content directly in an existing component or in a secondary component such as aTextField. Revisions that have been entered into a secondary component are automatically copied into the original component when the session is complete. - Traversal: There are two kinds of traversal: internal and external. Internal traversal is the movement within a given component, such as the movement of the cursor between one cell and another cell. External traversal is the movement between one component and another in a
Form, such as the move from aTextFieldto aChoiceGroup.
Now, I'll put some of these exciting new features to work building a custom GUI element.
The CheckItem element is a simple GUI element that gives you a choice of any two options; in the case of my example it will be Male or Female. You likely have some experience with this type of element, which is normally created using the standard MIDP class ChoiceGroup. My CheckItem class will emulate the functionality of the ChoiceGroup, but I'll develop it from scratch using the CustomItem class.
Figure 1 shows a Form that includes two TextItems and our simple CheckItem element.
Figure 1. The CheckItem element

In Listing 1 you can see the skeleton of my new CheckItem class. Because CustomItem is an abstract class it must be subclassed to enable the desired functionality. Because I want my CheckItem to be able to accept commands, such as Edit, it will implement the ItemCommandListener class. The ItemCommandListener class was introduced with the MIDP 2.0 specification. It enables me to set command listeners for a particular item on a form, rather than setting one listener for the whole form.
Listing 1. The beginnings of a CheckItem class
package com.kontio;
import javax.microedition.lcdui.*;
public class CheckItem
extends CustomItem
implements ItemCommandListener {
public CheckItem (String title, Display d, String caption1, String caption2) {
}
protected int getMinContentHeight() {
}
protected int getMinContentWidth() {
}
protected int getPrefContentHeight(int width) {
}
protected int getPrefContentWidth(int height) {
}
protected void paint(Graphics g, int w, int h) {
}
protected boolean traverse(int dir, int viewportWidth, int viewportHeight,
int[] visRect_inout) {
return true;
}
public void commandAction(Command c, Item i) {
}
protected void sizeChanged(int w, int h){
}
}
|
The get methods for minimum and preferred width and height are defined abstract in the CustomItem, so I have to implement them. In these methods I can either calculate or directly define the minimum and preferred width and height of each item. Another option that I have not utilized in this example would let me handle size change within the item, such as when the system reduces or expands the size of the available screen. The sizeChanged() method takes parameters that allow the application to redraw an item as its given size changes.
paint() is an abstract method whose job it is to paint the content of an item. The translation is set so that the upper left corner of my new item is at (0,0), so the application must paint every pixel within the item's area.
The traverse() method is called by the system whenever the user moves the cursor within or into the CheckItem. The traverse() method will return true if the traversal is internal and false if it should proceed out to another item in the form. Depending on the value returned, traverse() will either activate the item or just move the cursor around within the item. The method's parameters indicate the direction of traversal, the size of the viewable area, and other information about the area. It will be up to you to define when the traverse() method should return false, resulting in the activation of the next item on the form.
The CheckItem has two basic functions: it responds to the
movement of the cursor and sets the selected value (Male or Female in this example). Listing 2 shows the first part of CheckItem class, showing all the fields of the class, the implementation of the constructor, and the implementation of the get methods for minimum and preferred widths and heights. UPPER and LOWER are constants for the position of the cursor on the form. UPPER means that the cursor is on the form somewhere above the item, and LOWER means it's somewhere below the item. IN indicates that the cursor is inside the item. status is for keeping the position, location is the location of the item's own cursor, and checked is for the checked value.
Listing 2. The CheckItem class
public class CheckItem
extends CustomItem
implements ItemCommandListener {
private final static int UPPER = 0;
private final static int IN = 1;
private final static int LOWER = 2;
// status tells us where the cursor is on the form, UPPER, IN or LOWER
private int status = UPPER;
// location tells us where the cursor is inside the item
private int location = 1;
// checked tells which of the choices is selected
private int checked=1;
private String caption1;
private String caption2;
private final static Command CMD_EDIT = new Command("Edit", Command.ITEM, 1);
public CheckItem(String title, String caption1, String caption2) {
super(title);
this.caption1 = caption1;
this.caption2 = caption2;
setDefaultCommand(CMD_EDIT);
setItemCommandListener(this);
}
protected int getMinContentHeight() {
return 40;
}
protected int getMinContentWidth() {
return 95;
}
protected int getPrefContentHeight(int width) {
return 40;
}
protected int getPrefContentWidth(int height) {
return 95;
}
|
The constructor is quite simple; it sets the title, the captions, and the command object. For the sake of an example, the get methods in Listing 2 return constants. In real life, of course, I would have to calculate height and width according to the size of the content or the available area of the item.
In Listing 3 I show the second part of the CheckItem class. As previously mentioned, the paint() method is responsible for the visual appearance of the item. In this example, the method paints a rectangle around the element with a dotted line, although some emulators and devices will automatically do this for you.
Next, the paint() method draws the circles, cursor, and labels for my checkbox. In this case, the "selected" circle is a filled arc of 360 degrees. For a more sophisticated GUI, I might use images rather than pixels to denote the circles. The CheckItem class could also use the sizeChanged() method to match the size of the circles to the size of the screen, or to fit better with other items in the form.
Listing 3. The paint() method
protected void paint(Graphics g, int w, int h) {
// First the outer rectangle
g.setStrokeStyle(Graphics.DOTTED);
g.drawRect(0, 0, 94, 39);
g.setStrokeStyle(Graphics.SOLID);
// Then the circles
g.drawArc(4, 4, 13, 13, 0, 360);
g.drawArc(4, 22, 13, 13, 0, 360);
// Then draw the selected value
if (checked==1) g.fillArc(4, 4, 13, 13, 0, 360);
else g.fillArc(4, 22, 13, 13, 0, 360);
// Then the 'cursor'
if (location==1) g.drawRect(2, 2, 90, 17);
else g.drawRect(2, 20, 90, 17);
// And finally the texts
g.drawString(caption1, 22, 4, Graphics.TOP|Graphics.LEFT);
g.drawString(caption2, 22, 22, Graphics.TOP|Graphics.LEFT);
}
|
Listing 4 shows the third and final part of the CheckItem
class: the traverse() and commandAction() methods. The traverse() method is called whenever
the cursor has entered the item or traversal is occurring inside the item. The traverse() method is responsible for setting the inner state of the object so that it will be painted correctly. The parameter dir indicates the direction of traversal. Canvas.UP, Canvas.DOWN, Canvas.LEFT, Canvas.RIGHT, and CustomItem.NONE are all possible fields of the dir parameter.
In this case I ignore left and right and am only interested in UP and DOWN. If the direction is DOWN, the status will be checked. If the cursor has entered the item, the status will be set to IN. This ensures that the next traversal to occur will happen inside the element. If the cursor enters the element from above, and if it is the first traversal to occur in the form, the location will be set to 1, meaning the upper choice. As long as the traverse() method returns true, movement will occur within the element. When it returns false, the system can move the traversal outside of the element.
Listing 4. traversal() and commandAction()
protected boolean traverse(int dir, int viewportWidth, int viewportHeight,
int[] visRect_inout) {
switch (dir) {
case Canvas.DOWN:
if (status == UPPER) {
status = IN;
} else {
if (location==1) location=2;
else if (location==2) return false;
}
repaint();
break;
case Canvas.UP:
if (status == LOWER) {
status = IN;
} else {
if (location==2) location=1;
else if (location==1) return false;
}
repaint();
break;
}
return true;
}
public void commandAction(Command c, Item i) {
if (c == CMD_EDIT) {
checked=location;
repaint();
notifyStateChanged();
}
}
public int getState(){
return checked;
}
}
|
The commandAction() method sets the active choice checked. In the emulator this happens both by choosing the Edit command and pressing the Choose (fire) button. notifyStateChanged() causes the form to notify the item ItemStateListener that the item's state has been changed. The last method is for checking which of the choices is or was selected.
My new CheckItem class can be used and added to the form just like any other Item. For example, Figure 2 shows a
CheckItem implementation for a quiz with three questions.
Figure 2. A CheckItem implementation for a quiz

Listing 5 shows the CheckItemDemo class. In this
example the instances of CheckItem were created inside the append method, but they could just as easily have been created elsewhere.
Listing 5. The CheckItemDemo class
package com.kontio;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class CheckItemDemo extends MIDlet implements CommandListener {
private final static Command CMD_EXIT = new Command("Exit", Command.EXIT, 1);
private Display display;
protected void startApp() {
display = Display.getDisplay(this);
Form mainForm = new Form("CustomItemDemo");
mainForm.append(new CheckItem("How much is 8 divided by 2?", "3", "4"));
mainForm.append(new CheckItem("What is the capital of France?", "Paris", "Milano"));
mainForm.append(new CheckItem("Which is a mammal?", "Pike", "Dog"));
mainForm.addCommand(CMD_EXIT);
mainForm.setCommandListener(this);
display.setCurrent(mainForm);
}
public void commandAction(Command c, Displayable d) {
if (c == CMD_EXIT) {
destroyApp(false);
notifyDestroyed();
}
}
protected void destroyApp(boolean unconditional) {
}
protected void pauseApp() {
}
}
|
In real life, of course, it wouldn't make much sense to develop a
custom CheckItem class when you could simply use the
ChoiceGroup. The CheckItem was chosen merely for the sake of the exercise. To continue learning about MIDP 2.0,
you may want to apply some of what you've learned here to building more useful custom elements, such as spreadsheets, image maps, and charts. See Resources to learn more about MIDP 2.0.
- Visit the MIDP homepage to download MIDP 2.0.
- OnJava.com's "Introducing MIDP 2.0" is a good starting place for learning about the new features of MIDP 2.0.
- You can follow that up with Michael Abernethy's excellent primer, "Mobile device optimization with J2ME", which includes an overview of MIDP 2.0. (developerWorks, January 2003)
- If you're completely new to MIDP (and J2ME), you may want to read Todd Sundsted's
"J2ME grows up", which includes a good introduction to MIDP and MIDlets. (developerWorks, May 2001)
- Soma Ghosh's "Think small with J2ME" offers an architectural overview of J2ME, including the role of profiles. (developerWorks, November 2001)
- "Networking with J2ME" focuses on the networking capabilities of MIDP. (developerWorks, September 2002)
Mikko Kontio works as a Technology Manager for the leading-edge Finnish software company, Softera. He holds a Masters degree in Computer Science and is the author and co-author of several books, the latest being Professional Mobile Java with J2ME, published by IT Press. Mikko can be reached at mikko.kontio@softera.fi.