The Java TM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search

Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components

## How to Use Trees

> With the `JTree` class, you can display hierarchical data. A `JTree` object doesn't actually contain your data; it simply provides a view of the data. Like any non-trivial Swing component, the tree gets data by querying its data model. Here's a picture of a tree:

> As the preceding figure shows, `JTree` displays its data vertically. Each row displayed by the tree contains exactly one item of data, which is called a node. Every tree has a root node from which all nodes descend. By default, the tree displays the root node, but you can decree otherwise. A node can either have children or not. We refer to nodes that can have children -- whether or not they currently have children -- as branch nodes. Nodes that can't have children are leaf nodes.

> Branch nodes can have any number of children. Typically, the user can expand and collapse branch nodes -- making their children visible or invisible -- by clicking them. By default, all branch nodes except the root node start out collapsed. A program can detect changes in branch nodes' expansion state by listening for tree expansion or tree-will-expand events, as described in How to Write a Tree Expansion Listener and How to Write a Tree-Will-Expand Listener.

> The rest of this section discusses the following topics:

> - Creating a Tree
> - Responding to Node Selection
> - Customizing a Tree's Display
> - Dynamically Changing a Tree
> - Creating a Data Model
> - The Tree API
> - Examples that Use Trees

### Creating a Tree

> Here is a picture of an application, the top half of which displays a tree in a scroll pane.

> > Try this:
> > 1. Compile and run the application. The source file is `TreeDemo.java`. The program also looks for several HTML files. See the examples index for links to all the files required by this example.
> >    See Getting Started with Swing if you need help compiling or running this application.
> > 2. Expand one or more nodes.
> >    You can do this by clicking the circle to the left of the item.
> > 3. Collapse a node.
> >    You do this by clicking the circle to the left of an expanded node.

> The following code, taken from `TreeDemo.java`, creates the `JTree` object:

> >     DefaultMutableTreeNode top =
> >         new DefaultMutableTreeNode("The Java Series");
> >     createNodes(top);
> >     final JTree tree = new JTree(top);
> >     ...
> >     JScrollPane treeView = new JScrollPane(tree);

> The code creates an instance of `DefaultMutableTreeNode` to serve as the root node for the tree. It then creates the rest of the nodes in the tree. After that, it creates the tree, specifying the root node as an argument to the `JTree` constructor. Finally, it puts the tree in a scroll pane, a common tactic because showing the full, expanded tree would otherwise require too much space.

> Here is the code that creates the nodes under the root node:

> >     private void createNodes(DefaultMutableTreeNode top) {
> >         DefaultMutableTreeNode category = null;
> >         DefaultMutableTreeNode book = null;

> >         category = new DefaultMutableTreeNode(
> >             "Books for Java Programmers");
> >         top.add(category);

> >         //original Tutorial
> >         book = new DefaultMutableTreeNode(new BookInfo
> >             ("The Java Tutorial: Object-Oriented "
> >              + "Programming for the Internet",
> >             "tutorial.html"));
> >         category.add(book);

> >         //Tutorial Continued
> >         book = new DefaultMutableTreeNode(new BookInfo
> >             ("The Java Tutorial Continued: The Rest of the JDK",
> >             "tutorialcont.html"));
> >         category.add(book);

> >         //JFC Swing Tutorial
> >         book = new DefaultMutableTreeNode(new BookInfo
> >             ("The JFC Swing Tutorial: "
> >              + "A Guide to Constructing GUIs",
> >             "swingtutorial.html"));
> >         category.add(book);

> >         //...add many more books for programmers...

> >         category = new DefaultMutableTreeNode(
> >             "Books for Java Implementers");
> >         top.add(category);

> >         //VM
> >         book = new DefaultMutableTreeNode(new BookInfo
> >             ("The Java Virtual Machine Specification",
> >              "vm.html"));
> >         category.add(book);

> >         //Language Spec
> >         book = new DefaultMutableTreeNode(new BookInfo
> >             ("The Java Language Specification",
> >              "jls.html"));
> >         category.add(book);
> >     }

> The argument to the `DefaultMutableTreeNode` constructor is the user object -- an object that contains or points to the data associated with the tree node. The user object can be a string, or it can be a custom object. If you implement a custom object, you should implement its `toString` method so that it returns the string to be displayed for that node.

> For example, the `BookInfo` class used in the previous code snippet is a custom class that holds two pieces of data: the name of a book, and the URL for an HTML file describing the book. The `toString` method is implemented to return the book name. Thus, each node associated with a `BookInfo` object displays a book name.

> > Note: Swing 1.1.1 Beta 1 introduced the ability to specify HTML text for the string displayed by tree nodes. See Using HTML on a Label for details.

> To summarize, you can create a tree by invoking the `JTree` constructor, specifying the root node as an argument. You should probably put the tree inside a scroll pane, so that the tree won't take up too much space. You don't have to do anything to make the tree nodes expand and collapse in response to user clicks. However, you do have to add some code to make the tree respond when the user selects a node -- by clicking the node, for example.

### Responding to Node Selection

> Responding to tree node selections is simple. You implement a tree selection listener and register it on the tree, The following code shows the selection-related code from the `TreeDemo` program:

> >     tree.getSelectionModel().setSelectionMode
> >             (TreeSelectionModel.SINGLE_TREE_SELECTION);

> >     //Listen for when the selection changes.
> >     tree.addTreeSelectionListener(new TreeSelectionListener() {
> >         public void valueChanged(TreeSelectionEvent e) {
> >             DefaultMutableTreeNode node = (DefaultMutableTreeNode)
> >                                tree.getLastSelectedPathComponent();

> >             if (node == null) return;

> >             Object nodeInfo = node.getUserObject();
> >             if (node.isLeaf()) {
> >                 BookInfo book = (BookInfo)nodeInfo;
> >                 displayURL(book.bookURL);
> >             } else {
> >                 displayURL(helpURL);
> >             }
> >         }
> >     });

> The preceding code performs these tasks:
> - Gets the default `TreeSelectionModel` for the tree, and then sets it up so that at most one tree node at a time can be selected.
> - Creates an event handler and registers it on the tree. The event handler is an object that implements the `TreeSelectionListener` interface.
> - In the event handler, determines which node is selected by invoking the tree's `getLastSelectedPathComponent` method.
> - Uses the `getUserObject` method to get the data associated with the node.
> For more details about handling tree selection events, see How to Write a Tree Selection Listener.

### Customizing a Tree's Display

> Here is a picture of some tree nodes, as drawn by the Java, Windows, and Motif Look & Feel implementations.

> As the preceding figures show, a tree conventionally displays an icon and some text for each node. You can customize these, as we'll show shortly.

> A tree typically also performs some look-and-feel-specific painting to indicate relationships between nodes. You can customize this painting in a limited way. First, you can use `tree.setShowsRootHandles(true)` to request that a tree's top-level nodes -- the root node (if it's visible) or its children (if not) -- have handles that let them be expanded or collapsed. Second, if you're using the Java Look & Feel, you can customize whether lines are drawn to show relationships between tree nodes.

> By default, the Java Look & Feel draws no lines between nodes. By setting the `JTree.lineStyle` client property of a tree, you can specify a different convention. For example, to request that the Java Look & Feel use horizontal lines to group nodes (as shown in the next figure), use the following code:

> >     tree.putClientProperty("JTree.lineStyle", "Horizontal");

> To specify that the Java Look & Feel should draw lines detailing the relationships between nodes (as shown in the next figure), use this code:

> >     tree.putClientProperty("JTree.lineStyle", "Angled");

> No matter what the look and feel, the default icon displayed by a node is determined by whether the node is a leaf and, if not, whether it's expanded. For example, in the Windows and Motif Look & Feel implementations, the default icon for each leaf node is a dot; in the Java Look & Feel, the default leaf icon is a paper-like symbol. In all the look-and-feel implementations we've shown, branch nodes are marked with folder-like symbols. The Windows Look & Feel even has different icons for expanded branches versus collapsed branches.

> You can easily change the default icon used for leaf, expanded branch, or collapsed branch nodes. To do so, you first create an instance of `DefaultTreeCellRenderer`. Next, specify the icons to use by invoking one or more of the following methods on the renderer: `setLeafIcon` (for leaf nodes), `setOpenIcon` (for expanded branch nodes), `setClosedIcon` (for collapsed branch nodes). If you want the tree to display no icon for a type of node, then specify `null` for the icon. Once you've set up the icons, use the tree's `setCellRenderer` method to specify that the `DefaultTreeCellRenderer` paint its nodes.

> > Note: In the Swing 1.0 releases, `DefaultTreeCellRenderer` used to be called `BasicTreeCellRenderer` and was part of the `com.sun.java.swing.plaf.basic` package.

> Here is an example, taken from `TreeIconDemo.java`:

> >     DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
> >     renderer.setLeafIcon(new ImageIcon("images/middle.gif"));
> >     tree.setCellRenderer(renderer);

> Here is what the resulting UI looks like:

> If you want finer control over the node icons or you want to provide tool tips, you can do so by creating a subclass of `DefaultTreeCellRenderer` and overriding the `getTreeCellRendererComponent` method. Because `DefaultTreeCellRenderer` is a subclass of `JLabel`, you can use any `JLabel` method -- such as `setIcon` -- to customize the `DefaultTreeCellRenderer`. Here is an example of creating a cell renderer that varies the leaf icon depending on whether the word "Tutorial" is in the node's text data. The renderer also specifies tool-tip text, as the bold lines show. You can find the entire example in `TreeIconDemo2.java`.

> >     //...where the tree is initialized:
> >     //Enable tool tips.
> >     ToolTipManager.sharedInstance().registerComponent(tree);
> >     ...
> >     tree.setCellRenderer(new MyRenderer());
> >     ...
> >     class MyRenderer extends DefaultTreeCellRenderer {
> >         ImageIcon tutorialIcon;

> >         public MyRenderer() {
> >             tutorialIcon = new ImageIcon("images/middle.gif");
> >         }

> >         public Component getTreeCellRendererComponent(
> >                             JTree tree,
> >                             Object value,
> >                             boolean sel,
> >                             boolean expanded,
> >                             boolean leaf,
> >                             int row,
> >                             boolean hasFocus) {

> >             super.getTreeCellRendererComponent(
> >                             tree, value, sel,
> >                             expanded, leaf, row,
> >                             hasFocus);
> >             if (leaf && isTutorialBook(value)) {
> >                 setIcon(tutorialIcon);
> >                 setToolTipText("This book is in the Tutorial series.");
> >             } else {
> >                 setToolTipText(null); //no tool tip
> >             }

> >             return this;
> >         }

> >         protected boolean isTutorialBook(Object value) {
> >             DefaultMutableTreeNode node =
> >                     (DefaultMutableTreeNode)value;
> >             BookInfo nodeInfo =
> >                     (BookInfo)(node.getUserObject());
> >             String title = nodeInfo.bookName;
> >             if (title.indexOf("Tutorial") >= 0) {
> >                 return true;
> >             }

> >             return false;
> >         }
> >     }

> Here is the result:

> You might be wondering how a cell renderer works. When a tree paints each node, neither the `JTree` nor its look-and-feel-specific implementation actually contains the code that paints the node. Instead, the tree uses the cell renderer's painting code to paint the node. For example, to paint a leaf node that has the string "The Java Programming Language", the tree asks its cell renderer to return a component that can paint a leaf node with that string. If the cell renderer is a `DefaultTreeCellRenderer`, then it returns a label that paints the default leaf icon followed by the string.

> A cell renderer only paints; it cannot handle events. If you want to add event handling to a tree, you need to register your handler on either the tree or, if the handling occurs only when a node is selected, the tree's cell editor. For information about cell editors, see Concepts: Cell Editors and Renderers. That section discusses table cell editors and renderers, which are similar to tree cell editors and renderers.

### Dynamically Changing a Tree

> The following figure shows an application that lets you add nodes to and remove nodes from a visible tree. You can also edit the text in each node.

> The application is based on an example provided by tutorial reader Richard Stanford. You can find the source code in `DynamicTreeDemo.java` and `DynamicTree.java`. Here is the code that initializes the tree:

> >     rootNode = new DefaultMutableTreeNode("Root Node");
> >     treeModel = new DefaultTreeModel(rootNode);

> >     tree = new JTree(treeModel);
> >     tree.setEditable(true);
> >     tree.getSelectionModel().setSelectionMode
> >             (TreeSelectionModel.SINGLE_TREE_SELECTION);
> >     tree.setShowsRootHandles(true);

> By explicitly creating the tree's model, the code guarantees that the tree's model is an instance of `DefaultTreeModel`. That way, we know all the methods that the tree model supports. For example, we know that we can invoke the model's `insertNodeInto` method, even though that method is not required by the `TreeModel` interface.

> To make the text in the tree's nodes editable, we invoke `setEditable(true)` on the tree. When the user has finished editing a node, the model generates a tree model event that tells any listeners that tree nodes have changed. To catch this event, we can implement a `TreeModelListener`. Here is an example of a tree model listener we implemented to detect when the user has typed in a new name for a tree node:

> >     ...
> >     treeModel.addTreeModelListener(new MyTreeModelListener());
> >     ...
> >     class MyTreeModelListener implements TreeModelListener {
> >         public void treeNodesChanged(TreeModelEvent e) {
> >             DefaultMutableTreeNode node;
> >             node = (DefaultMutableTreeNode)
> >                      (e.getTreePath().getLastPathComponent());

> >             /*
> >              * If the event lists children, then the changed
> >              * node is the child of the node we've already
> >              * gotten.  Otherwise, the changed node and the
> >              * specified node are the same.
> >              */
> >             try {
> >                 int index = e.getChildIndices()[0];
> >                 node = (DefaultMutableTreeNode)
> >                        (node.getChildAt(index));
> >             } catch (NullPointerException exc) {}

> >             System.out.println("The user has finished editing the node.");
> >             System.out.println("New value: " + node.getUserObject());
> >         }
> >         public void treeNodesInserted(TreeModelEvent e) {
> >         }
> >         public void treeNodesRemoved(TreeModelEvent e) {
> >         }
> >         public void treeStructureChanged(TreeModelEvent e) {
> >         }
> >     }

> Here is the code that the Add button's event handler uses to add a new node to the tree:

> >     public void actionPerformed(ActionEvent e) {
> >        treePanel.addObject("New Node " + newNodeSuffix++);
> >     }
> >     ...
> >     public DefaultMutableTreeNode addObject(Object child) {
> >         DefaultMutableTreeNode parentNode = null;
> >         TreePath parentPath = tree.getSelectionPath();

> >         if (parentPath == null) {
> >             //There's no selection. Default to the root node.
> >             parentNode = rootNode;
> >         } else {
> >             parentNode = (DefaultMutableTreeNode)
> >                          (parentPath.getLastPathComponent());
> >         }

> >         return addObject(parentNode, child, true);
> >     }
> >     ...
> >     public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,
> >                                             Object child,
> >                                             boolean shouldBeVisible) {
> >         DefaultMutableTreeNode childNode =
> >                 new DefaultMutableTreeNode(child);
> >         ...
> >         treeModel.insertNodeInto(childNode, parent,
> >                                  parent.getChildCount());

> >         // Make sure the user can see the lovely new node.
> >         if (shouldBeVisible) {
> >             tree.scrollPathToVisible(new TreePath(childNode.getPath()));
> >         }
> >         return childNode;
> >     }

> The code creates a node, inserts it into the tree model, and then, if appropriate, requests that the nodes above it be expanded and the tree scrolled so that the new node is visible. To insert the node into the model, the code uses the `insertNodeInto` method provided by the `DefaultTreeModel` class.

### Creating a Data Model

> If `DefaultTreeModel` doesn't suit your needs, then you'll need to write a custom data model. Your data model must implement the `TreeModel` interface. `TreeModel` specifies methods for getting a particular node of the tree, getting the number of children of a particular node, determining whether a node is a leaf, notifying the model of a change in the tree, and adding and removing tree model listeners.

> Interestingly, the `TreeModel` interface accepts any kind of object as a tree node. It doesn't require that nodes be represented by `DefaultMutableTreeNode` objects, or even that nodes implement the `TreeNode` interface. Thus, if the `TreeNode` interface isn't suitable for your tree model, feel free to devise your own representation for tree nodes.

> The following figure shows an application that displays the descendents or ancestors of a particular person. (Thanks to tutorial reader Olivier Berlanger for providing this example.)

> You can find the custom tree model implementation in `GenealogyModel.java`. A simple `JTree` subclass that works with `GenealogyModel` is in `GenealogyTree.java`. The tree node class is defined in `Person.java`, and the application's GUI is created by the `main` method in `GenealogyExample.java`.

### The Tree API

> The tree API is quite extensive. The following tables list just a bit of the API, concentrating on the following categories:
> - Tree-Related Classes and Interfaces
> - Creating and Setting Up a Tree
> - Implementing Selection
> - Showing and Hiding Nodes

> For more information about the tree API, see the API documentation for `JTree` and for the various classes and interfaces in the tree package. Also refer to The JComponent Class for information on the API `JTree` inherits from its superclass.

> Tree-Related Classes and Interfaces
> Class/Interface | Purpose
> --- | ---
> `JTree` | The component that presents the tree to the user.
> `MutableTreeNode`, `DefaultMutableTreeNode` | The interfaces that the default tree model expects its tree nodes to implement, and the implementation used by the default tree model.
> `TreeModel`, `DefaultTreeModel` | Respectively, the interface that a tree model must implement and the usual implementation used.
> `TreeCellRenderer`, `DefaultTreeCellRenderer` | Respectively, the interface that a tree cell renderer must implement and the usual implementation used.
> `TreeCellEditor`, `DefaultTreeCellEditor` | Respectively, the interface that a tree cell editor must implement and the usual implementation used.
> `TreeSelectionModel`, `DefaultTreeSelectionModel` | Respectively, the interface that the tree's selection model must implement and the usual implementation used.
> `TreeSelectionListener`, `TreeSelectionEvent` | The interface and event type used for detecting tree selection changes. For more information, see How to Write a Tree Selection Listener.
> `TreeModelListener`, `TreeModelEvent` | The interface and event type used for detecting tree model changes. For more information, see How to Write a Tree Model Listener.
> `TreeExpansionListener`, `TreeWillExpandListener`, `TreeExpansionEvent` | The interfaces and event type used for detecting tree expansion and collapse. For more information, see How to Write a Tree Expansion Listener and How to Write a Tree-Will-Expand Listener.
> `ExpandVetoException` | An exception that a `TreeWillExpandListener` can throw to indicate that the impending expansion/collapse should not happen. For more information, see How to Write a Tree-Will-Expand Listener.

> Creating and Setting Up a Tree
> `JTree` Constructor/Method | Purpose
> --- | ---
> `JTree(TreeNode) JTree(TreeNode, boolean) JTree(TreeModel) JTree() JTree(Hashtable) JTree(Object[]) JTree(Vector)` | Create a tree. The `TreeNode` argument specifies the root node, to be managed by the default tree model. The `TreeModel` argument specifies the model that provides the data to the table. The `boolean` argument specifies how the tree should determine whether a node can have children. The no-argument version of this constructor is for use in builders; it creates a tree that contains some sample data. If you specify a `Hashtable`, array of objects, of `Vector` as an argument, then the argument is treated as a list of nodes under the root node (which is not displayed), and a model and tree nodes are constructed accordingly.
> `void setCellRenderer(TreeCellRenderer)` | Sets the renderer that draws each node.
> `void setEditable(boolean)`, `void setCellEditor(TreeCellEditor)` | The first method sets whether the user can edit tree nodes. By default, tree nodes are not editable. The second sets which customized editor to use.
> `void setShowsRootHandles(boolean)` | Sets whether the tree shows handles for its leftmost nodes, letting you expand and collapse the nodes. The default is false. If the tree doesn't show the root node, then you should invoke `setShowsRootHandles(true)`.

> Implementing Selection
> Method | Purpose
> --- | ---
> `void addTreeSelectionListener( TreeSelectionListener)` | Registers a listener to detect when the a node is selected or deselected.
> `void setSelectionModel(TreeSelectionModel) TreeSelectionModel getSelectionModel()` | Set or get the model used to control node selections. You can turn off node selection completely using `setSelectionModel(null)`.
> `void setSelectionMode(int) int getSelectionMode()` (in `TreeSelectionModel`) | Set or get the selection mode. The value can be `CONTIGUOUS_TREE_SELECTION`, `DISCONTIGUOUS_TREE_SELECTION`, or `SINGLE_TREE_SELECTION` (all defined in `TreeSelectionModel`).
> `Object getLastSelectionPathComponent()` | Get the object representing the currently selected node. This is equivalent to invoking `getLastPathComponent` on the value returned by `tree.getSelectionPath()`.
> `void setSelectionPath(TreePath) TreePath getSelectionPath()` | Set or get the path to the currently selected node.
> `void setSelectionPaths(TreePath[]) TreePath[] getSelectionPaths()` | Set or get the paths to the currently selected nodes.
> `void setSelectionPath(TreePath) TreePath getSelectionPath()` | Set or get the path to the currently selected node.

> Showing and Hiding Nodes
> Method | Purpose
> --- | ---
> `void addTreeExpansionListener( TreeExpansionListener)` `void addTreeWillExpandListener( TreeWillExpandListener)` | Register a listener to detect when the tree nodes have expanded or collapsed, or will be expanded or collapsed, respectively. To veto an impending expansion or collapse, a `TreeWillExpandListener` can throw a `ExpandVetoException`.
> `void expandPath(TreePath)` `void collapsePath(TreePath)` | Expand or collapse the specified tree path.
> `void scrollPathToVisible(TreePath)` | Ensures that the node specified by the path is visible -- that the path leading up to it is expanded and the node is in the scroll pane's viewing area.
> `void makeVisible(TreePath)` | Ensures that the node specified by the path is viewable -- that the path leading up to it is expanded. The node might not end up within the viewing area.
> `void setScrollsOnExpand(boolean)` `boolean getScrollsOnExpand()` | Set or get whether the tree attempts to scroll to show previous hidden nodes. The default value is true.

### Examples that Use Trees

> This table lists examples that use `JTree` and where those examples are described.

> Example | Where Described | Notes
> --- | --- | ---
> `TreeDemo` | Creating a Tree, Responding to Node Selection, Customizing a Tree's Display | Creates a tree that responds to user selections. It also has code for customizing the line style for the Java Look & Feel.
> `TreeIconDemo` | Customizing a Tree's Display | Adds a custom leaf icon to `TreeDemo`.
> `TreeIconDemo2` | Customizing a Tree's Display | Customizes certain leaf icons and also provides tool tips for certain tree nodes.
> `DynamicTreeDemo` | Dynamically Changing a Tree | Illustrates adding and removing nodes from a tree. Also allows editing of node text.
> `GenealogyTree.java`, `GenealogyModel.java` | Creating a Data Model | Implements a custom tree model.
> `TreeExpandEventDemo` | How to Write a Tree Expansion Listener | Shows how to detect node expansions and collapses.
> `TreeExpandEventDemo2` | How to Write a Tree-Will-Expand Listener | Shows how to veto node expansions.
> TreeTable, TreeTable II | Creating TreeTables in Swing, Creating TreeTables: Part 2 | Examples that combine a tree and table to show detailed information about a hierarchy such as a file system. The tree is a renderer for the table.

| Start of Tutorial > Start of Trail > Start of Lesson | Search