We get this question every now and again and generally our response is that we don't but would like to get such a contribution. However, looking at our samples it seems that most of our component building explanations revolve around "small" components rather than composites which would fit a tree much better.
A composite component is really a Container which we would assemble into a specialized component by tailoring together smaller components. Naturally we could also perform custom painting for such a composite but that often defeats the purpose...
These components aren't suitable for everything but for things such as a tree we gain several huge advantages by using Container nesting to build a tree:
- We represent the nodes using custom components allowing subclasses to manipulate the look of the tree to a great degree.
- We hardly need to write any code and styles, touch support, keyboard navigation etc. are all built in
- We can leverage transitions and component nesting for really stunning effects
Lets go strait to the code, first we need the model:
/**I think thats pretty self explanitory...
* Arranges tree node objects, a node can essentially be anything
*
* @author Shai Almog
*/
interface TreeModel {
/**
* Returns the child objects representing the given parent, null should return
* the root objects
*/
public Vector getChildren(Object parent);
/**
* Is the node a leaf or a folder
*/
public boolean isLeaf(Object node);
}
Then we would like the implementation:
class Node {Thats a bit long and tedious but its mostly unrelated to LWUIT, the main code is the tree component code which is really rather simple:
Object[] children;
String value;
public Node(String value, Object[] children) {
this.children = children;
this.value = value;
}
public String toString() {
return value;
}
}
TreeModel model = new TreeModel() {
Node[] sillyTree = {
new Node("Root 1", new Node[] {
new Node("Child 1", new Node[] {
new Node("Gand Child 1", new Node[] {
}),
new Node("Gand Child 2", new Node[] {
}),
new Node("Gand Child 3", new Node[] {
}),
new Node("Gand Child 4", new Node[] {
}),
}),
new Node("Child 2", new Node[] {
new Node("Something Else", new Node[] {
}),
new Node("More of the same", new Node[] {
}),
}),
new Node("Child 3", new Node[] {
}),
new Node("Child 4", new Node[] {
}),
}),
new Node("Root 2", new Node[] {
new Node("Something Else", new Node[] {
}),
new Node("More of the same", new Node[] {
}),
}),
new Node("Root 3", new Node[] {
new Node("Something Else", new Node[] {
}),
new Node("More of the same", new Node[] {
}),
}),
new Node("Root 4", new Node[] {
}),
};
/**Then all we need is to use it in order to produce the video on the right:
* LWUIT Tree component sample
*
* @author Shai Almog
*/
public class TreeComponent extends Container {
private static final String KEY_OBJECT = "TREE_OBJECT";
private static final String KEY_PARENT = "TREE_PARENT";
private static final String KEY_EXPANDED = "TREE_NODE_EXPANDED";
private static final String KEY_DEPTH = "TREE_DEPTH";
private ActionListener expansionListener = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
Component c = (Component)evt.getSource();
Object e = c.getClientProperty(KEY_EXPANDED);
if(e != null && e.equals("true")) {
collapseNode(c);
} else {
expandNode(c);
}
}
};
private TreeModel model;
private Image folder;
private Image nodeImage;
private int depthIndent = 15;
public TreeComponent(TreeModel model) {
try {
this.model = model;
folder = Image.createImage("/folder.png");
nodeImage = Image.createImage("/page_white.png");
setLayout(new BoxLayout(BoxLayout.Y_AXIS));
buildBranch(null, 0, this);
setScrollableY(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void expandNode(Component c) {
c.putClientProperty(KEY_EXPANDED, "true");
int depth = ((Integer)c.getClientProperty(KEY_DEPTH)).intValue();
Container parent = c.getParent();
Object o = c.getClientProperty(KEY_OBJECT);
Container dest = new Container(new BoxLayout(BoxLayout.Y_AXIS));
Label dummy = new Label();
parent.addComponent(BorderLayout.CENTER, dummy);
buildBranch(o, depth, dest);
parent.replace(dummy, dest, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, true, 300));
}
private void collapseNode(Component c) {
c.putClientProperty(KEY_EXPANDED, null);
Container p = c.getParent();
for(int iter = 0 ; iter < p.getComponentCount() ; iter++) {
if(p.getComponentAt(iter) != c) {
Label dummy = new Label();
p.replaceAndWait(p.getComponentAt(iter), dummy, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, false, 300));
p.removeComponent(dummy);
}
}
}
/**
* Adds the child components of a tree branch to the given container.nt
*/
protected void buildBranch(Object parent, int depth, Container destination) {
Vector children = model.getChildren(parent);
int size = children.size();
Integer depthVal = new Integer(depth + 1);
for(int iter = 0 ; iter < size ; iter++) {
Object current = children.elementAt(iter);
Button nodeComponent = createNodeComponent(current, depth);
if(model.isLeaf(current)) {
destination.addComponent(nodeComponent);
} else {
Container componentArea = new Container(new BorderLayout());
componentArea.addComponent(BorderLayout.NORTH, nodeComponent);
destination.addComponent(componentArea);
nodeComponent.addActionListener(expansionListener);
}
nodeComponent.putClientProperty(KEY_OBJECT, current);
nodeComponent.putClientProperty(KEY_PARENT, parent);
nodeComponent.putClientProperty(KEY_DEPTH, depthVal);
}
}
protected Button createNodeComponent(Object node, int depth) {
Button cmp = new Button(node.toString());
if(model.isLeaf(node)) {
cmp.setIcon(nodeImage);
} else {
cmp.setIcon(folder);
}
updateNodeComponentStyle(cmp.getSelectedStyle(), depth);
updateNodeComponentStyle(cmp.getUnselectedStyle(), depth);
return cmp;
}
protected void updateNodeComponentStyle(Style s, int depth) {
s.setBorder(null);
s.setPadding(LEFT, depth * depthIndent);
}
}
Form treeForm = new Form("Tree Test");
treeForm.setLayout(new BorderLayout());
TreeComponent tree = new TreeComponent(model);
treeForm.addComponent(BorderLayout.CENTER, tree);
treeForm.setScrollable(false);
treeForm.show();
0 comments:
Post a Comment