Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Custom Item Traversal in MIDP 2.0

 
By Eric Giguere, July 19, 2004  

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.