×
Namespaces

Variants
Actions

Drawing Collapsible Trees in Java ME

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata
Article
Created: jappit (15 Apr 2008)
Last edited: hamishwillee (08 Aug 2013)

This code will show how to draw trees with Java ME with the following features:

  • collapsible/expandable nodes
  • vertical scrolling

J2me collapsible tree.png

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;
}
}
This page was last modified on 8 August 2013, at 06:01.
71 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×