- The Tree Control
- Tree Appearance
- The TreeNode Interface
- The MutableTreeNode Interface
- The DefaultMutableTreeNode Class
- The TreePath Class
- What is a Leaf?
- Tree Expansion and Traversal
- Expanding and Collapsing Nodes under Program Control
- Tree Expansion Events
- Making Nodes Visible
- Controlling Node Expansion and Collapse
- Tree Model Events
- Implementation Plan for the File System Control
- File System Tree Control Implementation
- Using the File System Tree Control
- Custom Tree Rendering and Editing
- Customizing the Default Tree Cell Renderer
- ToolTips and Renderers
- Custom Cell Editors
- Controlling Which Nodes Can Be Edited
- Controlling Editability by Subclassing JTree
- Programmatic Control of Editors
- Editing Trees with Custom User Objects
- The valueForPathChanged Method
- The Tree Implementation
- The Cell Editor
- The Cell Renderer
What is a Leaf?
The Metal look-and-feel displays nodes that it considers to be leaves using what looks like a sheet of paper instead of a folder icon. How does it determine that the node is a leaf? There are two methods in the TreeNode interface that determine whether a node is a leaf:
public boolean getAllowsChildren() public boolean isLeaf()
If you think of the tree of Apollo and Skylab missions and their astronauts, the astronauts are represented as leaf objects. This is correct, because it is not our intention to add further nodes below those representing the astronauts. In cases like this, it is acceptable to determine that a node is a leaf simply because it doesn't actually have any children. Contrast this, however, with a file system. If a file system were represented by a tree control (an example of which you'll see later), you would expect files to be leaf objects, but you would always expect a directory to be represented as a folder, whether or not it contain any files or subdirectories. In this case, it is not sufficient to rely on the number of children that the node has.
If relying on the count of children is acceptable to determine whether a node is a leaf, the isLeaf method returns the appropriate answer, because the default implementation in DefaultMutableTreeNode returns true if the node that it is applied to does not have children and false if it does. This explains why the astronauts all appeared as leaf nodes even though nothing special was done when creating their nodes.
For nodes like directories, a more appropriate check is not whether the node has any child nodes, but whether it is allowed to have child nodes attached to it. If it isn't, then it must be a leaf and vice versa. The getAl-lowsChildren method is used to allow the node to return this information. DefaultMutableTreeNode supplies a default implementation that returns the value of the node's allowsChildren variable, which is initially true, indicating that the node is not a leaf.
The next problem, given that there are two ways to find out whether a node is a leaf, is how the tree knows which method is the appropriate one to use. This problem is handled by DefaultTreeModel, which maintains a boolean instance variable called asksAllowsChildren, initialized to false by default. DefaultTreeModel has two constructors:
public DefaultTreeModel(TreeNode rootNode); public DefaultTreeModel(TreeNode rootNode, boolean asksAllowsChildren)
If the second constructor is used, you can supply true as the second argument to indicate that all nodes in the model should have their getAl-lowsChildren method called to determine whether they should be considered a leaf. If asksAllowsChildren is false, the node's isLeaf method is always used. To achieve the correct effect, you need to use the correct tree model constructor and set this variable appropriately.
The value of this variable is only used in the DefaultTreeModel isLeaf routine:
public boolean isLeaf(Object node);
This method checks the asksAllowsChildren variable and returns the value of either isLeaf or getAllowsChildren, having invoked it on the TreeNode given as the argument. This method, then, returns the correct value whichever way the tree nodes are used to indicate whether they are leaf nodes.
Despite appearances, the argument supplied to this method must be a TreeNode, or you'll get a ClassCastException.
If you are using the getAllowsChildren method to determine whether a node is a leaf, you need to set the node's allowsChildren value properly when you create it. Alternatively, you can use the setAllowsChildren method to change it after construction.
Expanding and Collapsing a Tree
Even if you never change the content of a tree after creating it, trees are not static objects. Once it has been displayed, the user can change a tree's appearance by expanding and collapsing nonleaf nodes to show or hide different levels of the tree. This section shows you what possibilities there are for expanding and traversing a tree and looks at the events that these actions generate.