Tree

The LWUIT tree is remarkably similar to the table in its design. It however represents a hierarchical view of data such as a filesystem. In that sense a tree must be provided with a model to represent the underlying data. It is assumed that the underlying data is already "hierarchic" in its nature, such as a corporate structure or a file system.

The tree model exists as an interface for this reason alone. Building it as a class does not make sense for the common use case of a domain specific data model. To create a tree model one must implement the two methods in the interface: getChildren and isLeaf.

getChildren is the "heavy lifter" within the interface. It has one argument for the parent node and returns the children of this node as a vector. This method is called with a null argument for its parent representing the "root" of the tree (which is not displayed). From that point forward all calls to the method will be with objects returned via this method (which are not leafs).

isLeaf is trivial. It just indicates whether the object within a tree is a leaf node that has no children and can not be expanded.

For example, the Tree would invoke getChildren(null) and receive back the String's "X", "Y' and "Z" within the return vector. It would then call isLeaf("X"), isLeaf("Y"), isLeaf("Z") and render the tree appropriately (as parent nodes or as leaves based on the response to isLeaf).

If the user clicks the "X" node and it is not a leaf the tree expands to contain (in addition to the existing nodes) the response for getChildren("X") as subnodes of "X".

Most of the code below relates to the model. It would be more domain specific for any specific case.

class Node {
    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("X", new Node[] {
            new Node("Child 1", new Node[] {
            }),
            new Node("Child 2", new Node[] {
            }),
            new Node("Child 3", new Node[] {
            }),
        }),
        new Node("Y", new Node[] {
            new Node("A", new Node[] {
            })
        }),
        new Node("Z", new Node[] {
            new Node("A", new Node[] {
            }),
        }),
    };

    public Vector getChildren(Object parent) {
        Node n = (Node)parent;
        Object[] nodes;
        if(parent == null) {
            nodes = sillyTree;
        } else {
            nodes = n.children;
        }
        Vector v = new Vector();
        for(int iter = 0 ; iter < nodes.length ; iter++) {
            v.addElement(nodes[iter]);
        }
        return v;
    }

    public boolean isLeaf(Object node) {
        Node n = (Node)node;
        return n.children == null || n.children.length == 0;
    }
};

Form treeForm = new Form("Tree");
treeForm.setLayout(new BorderLayout());
treeForm.addComponent(BorderLayout.CENTER, new Tree(model));
treeForm.show();

The tree created by the example code is shown in Figure 1. (In the example image, X and Z have been expanded by the user.)

Figure: Tree example

Customising the tree

The tree has special static methods to determine icons appropriate for expanded or folded folder and leaf nodes: setFolderOpenIcon(Image), setFolderIcon(Image), setNodeIcon(Image).

Besides that, one can derive the tree component and override the createNodeComponent method to customise the returned component in any desired way.