Custom user interface components can display arbitrarily complex data. You might use a custom component to display a chart, a list, or a table.
In almost every case, you need the ability to navigate within the component. Selecting an individual cell within a table is an obvious
example. Users must also be able to navigate between components, to move the input focus from one component to another. In version 2.0 of the Mobile Information Device Profile (MIDP), this inter-component and intra-component navigation is called traversal. Understanding how
traversal works and how it's exposed to the user is vital to writing usable custom components.
As the earlier tech tip "
Using Custom Items in MIDP 2.0" described, in
MIDP 2.0 all custom components derive from the
javax.microedition.lcdui.CustomItem class, itself an extension of the
MIDP 1.0 javax.microedition.lcdui.Item class. As with any item, many of the display and interaction details of custom components you create will be out of your control, and vary from device to device. Users can trigger traversal in a variety of ways: pressing dedicated scroll
buttons, turning a thumbwheel, and tapping on an area with a stylus are obvious examples. Each device's MIDP implementation insulates your
component from these details, though, and chooses the interaction methods that best suit the device. A component can deal with traversal only in the abstract.
Three methods of CustomItem are crucial to defining traversal behavior. The first, getInteractionModes(), was covered earlier in the tech tip "
Interaction Modes in MIDP 2.0," but briefly: A custom component calls this method at runtime to discover what kinds of traversal the device
supports. If the returned bit mask includes
CustomItem.TRAVERSE_HORIZONTAL, the device supports horizontal traversal. If the mask includes CustomItem.TRAVERSE_VERTICAL, vertical traversal is supported. Most components don't need to know which traversal directions are supported by the system, but some do. For
example, navigation within a table is handled differently if the device doesn't support traversal in two dimensions.
The second method that concerns us is traverseOut(), called by the
system whenever the user traverses away from the component. Your component class overrides this method and resets its state accordingly,
and may repaint itself in the process:
private boolean inTraversal;
protected void traverseOut(){
inTraversal = false;
repaint();
}
|
Note that if your component does no internal traversal, selection, or highlighting, you don't have to implement this method, because the
system always draws the appropriate highlighting or other decoration to indicate whether the component is selected or not. Note that traversal
out of a component can occur for any of a number of reasons, including the display of a new screen.
The third method, traverse(), is where all the action happens. The system calls this method whenever a traversal event occurs:
protected boolean traverse( int direction,
int viewportWidth,
int viewportHeight,
int[] visRect_inOut ){
// handle the traversal
}
|
Note that traverse() is called whether the traversal is into the
component or within it, so you must track the traversal state to deal properly with both cases.
The initial call to traverse() occurs immediately after the system selects the item. If the component supports internal traversal, the
method should return true to indicate that it should continue to receive traversal events. If the component doesn't support internal traversal, the method should return false, as the default implementation does.
On non-initial calls, traverse() should return false when it needs to
indicate that any internal traversal is complete, and that the system should traverse to the next component instead - if possible: Remember
that execution doesn't actually leave the component until traverseOut()
is called. To indicate that internal traversal is under way, and that the component can continue to receive traversal events, traverse()
should return true.
The first parameter to traverse() is the most important, as it indicates the traversal direction. The argument passed may be a game-action value
- Canvas.UP, Canvas.DOWN, Canvas.LEFT, or Canvas.RIGHT - or it may be
the special value CustomItem.NONE. The latter is passed whenever the user directly selects an item without indicating any kind of scrolling or traversal direction. The second and third parameters describe the width and height of the component's viewable area, called the viewport. The viewport may be smaller than the actual size of the component, so its size is needed to perform scrolling properly. The final parameter to traverse() is an array of integers holding the coordinates of the
component's visible area - x, y, width, and height - which the system uses to position or scroll the component. The component can update this array - generally the area corresponding to the internal traversal - to force a new part of the component to become visible. Note that any changes to the array are ignored if traverse() returns false.
The API documentation for traverse() contains an extensive discussion of how traversal of custom items works; be sure to consult it.
About the Author: Eric Giguere is a software developer for iAnywhere Solutions, a subsidiary of Sybase, where he works on Java technologies for handheld and wireless computing. He holds BMath and MMath degrees in Computer Science from the University of Waterloo and has written extensively on computing topics.