J2ME Drawing Collapsible Trees

This code will show how to draw trees with J2me with the following features:
  • collapsible/expandable nodes
  • vertical scrolling

Image: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;
}
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值