Drawing Collapsible Trees in Java ME
Article Metadata
This code will show how to draw trees with Java ME with the following features:
- collapsible/expandable nodes
- vertical scrolling
You can see a sample midlet showing this code in action on this page.
MenuNode class
To start, we need a class to represent single tree nodes. This class will have also features related to its graphic appearance: even if it's not a best practice, we'll do that to reduce the number of classes within our J2me code.
import java.util.Vector;
public class MenuNode
{
/* graphic properties */
public int x = 0;
public int y = 0;
/* node properties */
public MenuNode parentNode = null;
public String label = null;
public boolean expanded = false;
public int index = 0;
Vector children = null;
public MenuNode(String label)
{
this.label = label;
this.children = new Vector();
}
public void appendChild(MenuNode node)
{
node.parentNode = this;
node.index = this.children.size();
this.children.addElement(node);
}
public int getChildrenNum()
{
return this.children.size();
}
public MenuNode getNextSibling()
{
if(parentNode != null)
{
return parentNode.getChild(index + 1);
}
return null;
}
public MenuNode getPrevSibling()
{
if(parentNode != null)
{
return parentNode.getChild(index - 1);
}
return null;
}
public MenuNode getLastChild()
{
return getChild(getChildrenNum() - 1);
}
public MenuNode getChild(int i)
{
if(i >= 0 && i < this.children.size())
{
return (MenuNode)this.children.elementAt(i);
}
return null;
}
public void removeChildren(int index)
{
this.children.removeElementAt(index);
for(int i = index; i < this.children.size(); i++)
{
this.getChild(i).index--;
}
}
public boolean hasChildren()
{
return this.getChildrenNum() > 0;
}
public void expand()
{
if(this.getChildrenNum() > 0)
{
this.expanded = true;
}
}
public void collapse()
{
this.expanded = false;
}
}
TreeMenu
Now, we'll write down our TreeMenu component, that will accept a root node, width and height within its constructor, and then will manage user key actions through the keyAction() method. The only other public method will be paint(), which will paint our tree on the specified Graphic object.
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
public class ScrollableIconTreeMenu
{
Font font = Font.getDefaultFont();
int foreColor = 0x000000;
int foreFocusedColor = 0xff0000;
int viewportHeight = 0;
int viewportWidth = 0;
int viewportY = 0;
int nodeHeight = 0;
int childPadding = 10;
int verticalPadding = 2;
MenuNode root = null;
MenuNode current = null;
Image expandIcon = null;
Image collapseIcon = null;
Image leafIcon = null;
public static final int ICON_WIDTH = 6;
public static final int ICON_HEIGHT = 6;
public ScrollableIconTreeMenu(MenuNode root, int width, int height)
{
try
{
expandIcon = Image.createImage(getClass().getResourceAsStream("/plus.png"));
collapseIcon = Image.createImage(getClass().getResourceAsStream("/minus.png"));
leafIcon = Image.createImage(getClass().getResourceAsStream("/leaf.png"));
nodeHeight = Math.max(ICON_HEIGHT, font.getHeight());
}
catch(Exception e)
{
}
this.viewportHeight = height;
this.viewportWidth = width;
this.root = root;
this.current = root;
}
public void keyAction(int key)
{
switch(key)
{
case Canvas.LEFT:
keyLeft();
break;
case Canvas.UP:
keyUp();
break;
case Canvas.RIGHT:
keyRight();
break;
case Canvas.DOWN:
keyDown();
break;
}
}
void keyLeft()
{
MenuNode nextNode = null;
if(current.expanded)
{
current.collapse();
}
else
{
nextNode = current.parentNode;
}
setCurrentNode(nextNode);
}
void keyRight()
{
MenuNode nextNode = null;
if(current.expanded)
{
nextNode = current.getChild(0);
}
else
{
current.expand();
}
setCurrentNode(nextNode);
}
void keyDown()
{
MenuNode nextNode = null;
if(current.expanded)
{
nextNode = current.getChild(0);
}
if(nextNode == null)
{
MenuNode searchingSibling = current;
while(searchingSibling != null && nextNode == null)
{
nextNode = searchingSibling.getNextSibling();
searchingSibling = searchingSibling.parentNode;
}
}
setCurrentNode(nextNode);
}
void keyUp()
{
MenuNode nextNode = this.current.getPrevSibling();
if(nextNode == null)
{
nextNode = current.parentNode;
}
else
{
while(nextNode.expanded)
{
nextNode = nextNode.getLastChild();
}
}
setCurrentNode(nextNode);
}
void setCurrentNode(MenuNode node)
{
if(node != null)
{
this.current = node;
if(this.current.y < this.viewportY)
{
this.viewportY = this.current.y;
}
else if(this.current.y + font.getHeight() > this.viewportY + this.viewportHeight)
{
this.viewportY = this.current.y + font.getHeight() - this.viewportHeight;
}
}
}
public void paint(Graphics g)
{
int cx, cy, cw, ch;
cx = g.getClipX();
cy = g.getClipY();
cw = g.getClipWidth();
ch = g.getClipHeight();
g.setClip(0, 0, viewportWidth, viewportHeight);
g.translate(0, - viewportY);
paintNode(g, root, 0, 0);
g.translate(0, viewportY);
g.setClip(cx, cy, cw, ch);
}
int paintNode(Graphics g, MenuNode node, int left, int top)
{
node.x = left;
node.y = top;
Image icon = node.hasChildren() ?
(node.expanded ? collapseIcon : expandIcon) :
leafIcon;
g.drawImage(icon, left, top + nodeHeight / 2, Graphics.LEFT | Graphics.VCENTER);
if(node == current)
{
g.setColor(foreFocusedColor);
}
else
{
g.setColor(foreColor);
}
g.drawString(node.label, left + 2 + ICON_WIDTH, top + (nodeHeight - font.getHeight()) / 2, Graphics.TOP | Graphics.LEFT);
int nodeHeight = 0;
nodeHeight += font.getHeight() + verticalPadding;
if(node.expanded)
{
left += childPadding;
int childrenNum = node.getChildrenNum();
for(int i = 0; i < childrenNum; i++)
{
nodeHeight += paintNode(g, node.getChild(i), left, top + nodeHeight);
}
left -= childPadding;
}
return nodeHeight;
}
}



26 Sep
2009
This article presents a tree UI component for Java ME. Such components are commonly used in desktop applications as they are useful for presenting hierarchical information. However, no standard tree UI component is available to programmers as part of the MIDP classes. The author presents a tree component which allows programmers to display hierarchical data in a tree format which allows users to expand/collapse nodes and scroll through the tree.
While the code is not very well commented, the tree component is nicely organised into two classes, one which contains the logic for a single tree node and the other which contains the logic and presentation code for the entire tree. The code is well written and the use of meaningful method and field names make the code easy to follow and understand. It should be easy for another programmer to make use of this component and customize it as necessary. A link is also provided to see the component in action, showing it to work as specified. Perhaps one shortcoming of the component is that if node labels are too long, the component does not appear to provide logic to wrap text or provide horizontal scrolling functionality.
This article presents a useful UI component which will allow designers of Java ME midlets to present hierarchical data in an intuitive format.