Documentation

The Java™ Tutorials
Hide TOC
How to Use Trees如何使用树
Trail: Creating a GUI With Swing
Lesson: Using Swing Components
Section: How to Use Various Components

How to Use Trees如何使用树

With the JTree class, you can display hierarchical data. 使用JTree类,可以显示分层数据。A JTree object does not actually contain your data; it simply provides a view of the data. JTree对象实际上并不包含您的数据;它只是提供数据的视图。Like any non-trivial Swing component, the tree gets data by querying its data model. 与任何非平凡的Swing组件一样,树通过查询其数据模型来获取数据。Here is a picture of a tree:这是一棵树的图片:

A tree

As the preceding figure shows, JTree displays its data vertically. 如上图所示,JTree垂直显示其数据。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 not 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.程序可以通过侦听树扩展或树将扩展事件来检测分支节点扩展状态的变化,如如何编写树扩展侦听器如何编写树将扩展侦听器中所述。

A specific node in a tree can be identified either by a TreePath, an object that encapsulates a node and all of its ancestors, or by its display row, where each row in the display area displays one node.树中的特定节点可以通过树路径(封装节点及其所有祖先的对象)或其显示行来标识,其中显示区域中的每一行显示一个节点。

The rest of this section discusses the following topics:本节其余部分讨论以下主题:

Creating a Tree创建树

Here is a picture of an application, the top half of which displays a tree in a scroll pane.这是一个应用程序的图片,它的上半部分在滚动窗格中显示一个树。

TreeDemo

Try this: 
  1. Click the Launch button to run the Tree Demo using Java™ Web Start (download JDK 7 or later). 单击启动按钮,使用Java™Web启动运行树演示(下载JDK 7或更高版本)。Alternatively, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引Launches the TreeDemo example

  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 and puts it in a scroll pane:以下代码取自TreeDemo.java,创建JTree对象并将其放在滚动窗格中:

//Where instance variables are declared:
private JTree tree;
...
public TreeDemo() {
    ...
    DefaultMutableTreeNode top =
        new DefaultMutableTreeNode("The Java Series");
    createNodes(top);
    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. 代码创建一个DefaultMutableTreeNode实例作为树的根节点。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. 之后,它创建树,指定根节点作为JTree构造函数的参数。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: A Short Course on the Basics",
        "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);
    
    //Swing Tutorial
    book = new DefaultMutableTreeNode(new BookInfo
        ("The Swing Tutorial: A Guide to Constructing GUIs",
        "swingtutorial.html"));
    category.add(book);

    //...add 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 which is an object that contains or points to the data associated with the tree node. DefaultMutableTreeNode构造函数的参数是用户对象,该对象包含或指向与树节点关联的数据。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. 如果实现自定义对象,则应实现其toString方法,以便它返回要为该节点显示的字符串。JTree, by default, renders each node using the value returned from toString, so it is important that toString returns something meaningful. 默认情况下,JTree使用从toString返回的值呈现每个节点,因此toString返回一些有意义的内容是很重要的。Sometimes, it is not feasible to override toString; in such a scenario you can override the convertValueToText of JTree to map the object from the model into a string that gets displayed.有时,覆盖toString是不可行的;在这种情况下,可以重写JTreeconvertValueToText,将对象从模型映射到显示的字符串。

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. 例如,前面代码段中使用的BookInfo类是一个自定义类,它包含两部分数据:书籍的名称和描述书籍的HTML文件的URL。The toString method is implemented to return the book name. 实现toString方法以返回图书名称。Thus, each node associated with a BookInfo object displays a book name.因此,与BookInfo对象关联的每个节点都显示一个图书名称。


Note: You can specify text formatting in a tree node by putting HTML tags in the string for the node. 您可以通过在节点的字符串中放置HTML标记来指定树节点中的文本格式。See Using HTML in Swing Components for details. 有关详细信息,请参阅在Swing组件中使用HTML

To summarize, you can create a tree by invoking the JTree constructor, specifying the class that implements TreeNode as an argument. 总之,您可以通过调用JTree构造函数来创建树,并将实现TreeNode的类指定为参数。You should probably put the tree inside a scroll pane, so that the tree would not take up too much space. 您可能应该将树放在滚动窗格中,这样树就不会占用太多空间。You do not 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:以下代码显示了TreeDemo程序中与选择相关的代码:

//Where the tree is initialized:
    tree.getSelectionModel().setSelectionMode
            (TreeSelectionModel.SINGLE_TREE_SELECTION);

    //Listen for when the selection changes.
    tree.addTreeSelectionListener(this);
...
public void valueChanged(TreeSelectionEvent e) {
//Returns the last path element of the selection.
//This method is useful only when the selection model allows a single selection.
    DefaultMutableTreeNode node = (DefaultMutableTreeNode)
                       tree.getLastSelectedPathComponent();

    if (node == null)
    //Nothing is selected.     
    return;

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

The preceding code performs these tasks:前面的代码执行这些任务:

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 Mac OS look and feel implementations.下面是一些树节点的图片,如Java、Windows和Mac OS外观实现所示。

TreeDemo with angled lines A tree in the Windows look and feel A tree in the MacOS look and feel
Java look and feel Windows look and feel Mac OS look and feel

As the preceding figures show, a tree conventionally displays an icon and some text for each node. 如上图所示,树通常为每个节点显示一个图标和一些文本。You can customize these, as we will 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.setRootVisible(true) to show the root node or tree.setRootVisible(false) to hide it. 首先,可以使用tree.setRootVisible(true)显示根节点,或者使用tree.setRootVisible(false)隐藏根节点。Second, you can use tree.setShowsRootHandles(true) to request that a tree's top-level nodes — the root node (if it is visible) or its children (if not) — have handles that let them be expanded or collapsed.其次,您可以使用tree.setShowsRootHandles(true)请求树的顶级节点—根节点(如果可见)或其子节点(如果不可见)—具有可使其展开或折叠的句柄。

If you are using the Java look and feel, you can customize whether lines are drawn to show relationships between tree nodes. 如果使用的是Java外观,则可以自定义是否绘制线以显示树节点之间的关系。By default, the Java look and feel draws angled lines between nodes. 默认情况下,Java外观在节点之间绘制斜线。By setting the JTree.lineStyle client property of a tree, you can specify a different convention. 通过设置树的JTree.lineStyle客户端属性,可以指定不同的约定。For example, to request that the Java look and feel use only horizontal lines to group nodes, use the following code:例如,要请求Java外观仅使用水平线来分组节点,请使用以下代码:

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

To specify that the Java look and feel should draw no lines, use this code:要指定Java外观不应绘制线条,请使用以下代码:

tree.putClientProperty("JTree.lineStyle", "None");

The following snapshots show the results of setting the JTree.lineStyle property, when using the Java look and feel.以下快照显示了使用Java外观时设置JTree.lineStyle属性的结果。

TreeDemo with angled lines
TreeDemo with horizontal lines
TreeDemo with no lines
"Angled" (default) "Horizontal" "None"

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 is expanded. 无论外观如何,节点显示的默认图标都取决于节点是否为叶,如果不是,则取决于是否展开。For example, in the Windows and Motif look and feel implementations, the default icon for each leaf node is a dot; in the Java look and feel, the default leaf icon is a paper-like symbol. 例如,在Windows和Motif外观实现中,每个叶节点的默认图标是点;在Java外观中,默认的叶图标是一个纸质符号。In all the look-and-feel implementations we have shown, branch nodes are marked with folder-like symbols. 在我们展示的所有外观实现中,分支节点都用类似文件夹的符号标记。Some look and feels might have 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. 为此,首先创建DefaultTreeCellRenderer的实例。You could always create your own TreeCellRenderer implementation from scratch, reusing whatever components you like. 您总是可以从头开始创建自己的TreeCellRenderer实现,重用任何您喜欢的组件。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). 接下来,通过调用渲染器上的以下一个或多个方法来指定要使用的图标:setLeafIcon(用于叶节点)、setOpenIcon(用于展开的分支节点)和setClosedIcon(对于折叠的分支节点)。If you want the tree to display no icon for a type of node, then specify null for the icon. 如果希望树不显示某一类型节点的图标,请为该图标指定nullOnce you have set up the icons, use the tree's setCellRenderer method to specify that the DefaultTreeCellRenderer paint its nodes. 设置图标后,使用树的setCellRenderer方法指定DefaultTreeCellRenderer绘制其节点。Here is an example, taken from TreeIconDemo.java:下面是一个来自TreeIconDemo.java的示例:

ImageIcon leafIcon = createImageIcon("images/middle.gif");
if (leafIcon != null) {
    DefaultTreeCellRenderer renderer = 
        new DefaultTreeCellRenderer();
    renderer.setLeafIcon(leafIcon);
    tree.setCellRenderer(renderer);
}

Here is the screenshot of TreeIconDemo:以下是TreeIconDemo的截图:

TreeIconDemo

Try this: 

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. 如果您希望更好地控制节点图标或提供工具提示,可以通过创建DefaultTreeCellRenderer的子类并重写getTreeCellRendererComponent方法来实现。Because DefaultTreeCellRenderer is a subclass of JLabel, you can use any JLabel method — such as setIcon — to customize the DefaultTreeCellRenderer.因为DefaultTreeCellRendererJLabel的子类,所以可以使用任何JLabel方法;例如setIcon—自定义DefaultTreeCellRenderer

The following code, from TreeIconDemo2.java, creates a cell renderer that varies the leaf icon depending on whether the word "Tutorial" is in the node's text data. 下面的代码来自TreeIconDemo2.java,创建了一个单元格渲染器,根据节点文本数据中是否有“教程”一词来改变叶图标。The renderer also specifies tool-tip text, as the bold lines show. 渲染器还指定工具提示文本,如粗体线所示。


Try this: 
//...where the tree is initialized:
//Enable tool tips.
    ToolTipManager.sharedInstance().registerComponent(tree);
    
    ImageIcon tutorialIcon = createImageIcon("images/middle.gif");
    if (tutorialIcon != null) {
        tree.setCellRenderer(new MyRenderer(tutorialIcon));
    }
...
class MyRenderer extends DefaultTreeCellRenderer {
    Icon tutorialIcon;

    public MyRenderer(Icon icon) {
        tutorialIcon = icon;
    }

    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:结果如下:

TreeIconDemo2

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. 当树绘制每个节点时,JTree及其特定于外观的实现实际上都不包含绘制节点的代码。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. 例如,要绘制具有字符串“Java编程语言”的叶节点,树要求其单元渲染器返回一个可以使用该字符串绘制叶节点的组件。If the cell renderer is a DefaultTreeCellRenderer, then it returns a label that paints the default leaf icon followed by the string.如果单元格渲染器是DefaultTreeCellRenderer,则它返回一个标签,该标签绘制默认叶图标,后跟字符串。

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: 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 called DynamicTreeDemo that lets you add nodes to and remove nodes from a visible tree. 下图显示了一个名为DynamicReedemo的应用程序,该应用程序允许您向可见树添加节点和从可见树中删除节点。You can also edit the text in each node.还可以编辑每个节点中的文本。

DynamicTreeDemo

The application is based on an example provided by tutorial reader Richard Stanford. 该应用程序基于教程读者Richard Stanford提供的示例。


Try this: 

Here is the code that initializes the tree:下面是初始化树的代码:

rootNode = new DefaultMutableTreeNode("Root Node");
treeModel = new DefaultTreeModel(rootNode);
treeModel.addTreeModelListener(new MyTreeModelListener());

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. 通过显式创建树的模型,代码保证树的模型是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.例如,我们知道我们可以调用模型的insertNodeInto方法,即使TreeModel接口不需要该方法。

To make the text in the tree's nodes editable, we invoke setEditable(true) on the tree. 为了使树节点中的文本可编辑,我们在树上调用setEditable(true)When the user has finished editing a node, the model generates a tree model event that tells any listeners — including the JTree — that tree nodes have changed. 当用户完成编辑节点时,模型生成树模型事件,该事件告诉任何监听器;包括JTree—树节点已更改。Note that although DefaultMutableTreeNode has methods for changing a node's content, changes should go through the DefaultTreeModel cover methods. 请注意,尽管DefaultMutableTreeNode具有更改节点内容的方法,但更改应通过DefaultTreeModel覆盖方法进行。Otherwise, the tree model events would not be generated, and listeners such as the tree would not know about the updates.否则,将不会生成树模型事件,树之类的侦听器将不知道更新。

To be notified of node changes, we can implement a TreeModelListener. 为了通知节点更改,我们可以实现TreeModelListenerHere is an example of a tree model listener that detects when the user has typed in a new name for a tree node:下面是一个树模型侦听器的示例,用于检测用户何时为树节点键入了新名称:

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 have 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:下面是添加按钮的事件处理程序用于将新节点添加到树中的代码:

treePanel.addObject("New Node " + newNodeSuffix++);
...
public DefaultMutableTreeNode addObject(Object child) {
    DefaultMutableTreeNode parentNode = null;
    TreePath parentPath = tree.getSelectionPath();

    if (parentPath == null) {
        //There is 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.要将节点插入模型中,代码使用DefaultTreeModel类提供的insertNodeInto方法。

Creating a Data Model创建数据模型

If DefaultTreeModel does not suit your needs, then you will need to write a custom data model. 如果DefaultTreeModel不适合您的需要,则需要编写自定义数据模型。Your data model must implement the TreeModel interface. 数据模型必须实现TreeModel接口。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. 有趣的是,TreeModel接口接受任何类型的对象作为树节点。It does not require that nodes be represented by DefaultMutableTreeNode objects, or even that nodes implement the TreeNode interface. 它不要求节点由DefaultMutableTreeNode对象来表示,甚至不要求节点实现TreeNode接口。Thus, if the TreeNode interface is not suitable for your tree model, feel free to devise your own representation for tree nodes. 因此,如果TreeNode接口不适合您的树模型,请随意为树节点设计自己的表示。For example, if you have a pre-existing hierarchical data structure, you do not need to duplicate it or force it into the TreeNode mold. 例如,如果您有一个预先存在的分层数据结构,则不需要复制它或将其强制到TreeNode模型中。You just need to implement your tree model so that it uses the information in the existing data structure.您只需要实现树模型,以便它使用现有数据结构中的信息。

The following figure shows an application called GenealogyExample that displays the descendants or ancestors of a particular person. 下图显示了一个名为GenealogyExample的应用程序,它显示了特定人的后代或祖先。(Thanks to tutorial reader Olivier Berlanger for providing this example.) (感谢教程读者Olivier Berlanger提供此示例。)


Try this: 
GenealogyExample

You can find the custom tree model implementation in GenealogyModel.java. 您可以在GenealogyModel.java中找到自定义树模型实现。Because the model is implemented as an Object subclass instead of, say, a subclass of DefaultTreeModel, it must implement the TreeModel interface directly. 因为模型是作为Object子类而不是DefaultTreeModel的子类实现的,所以它必须直接实现TreeModel接口。This requires implementing methods for getting information about nodes, such as which is the root and what are the children of a particular node. 这需要实现获取节点信息的方法,例如哪个节点是根节点,哪个节点是特定节点的子节点。In the case of GenealogyModel, each node is represented by an object of type Person, a custom class that does not implement TreeNode.GenealogyModel情况下,每个节点由Person类型的对象表示,这是一个不实现TreeNode的自定义类。

A tree model must also implement methods for adding and removing tree model listeners, and must fire TreeModelEvents to those listeners when the tree's structure or data changes. 树模型还必须实现用于添加和删除树模型监听器的方法,并且必须在树的结构或数据更改时向这些监听器激发TreeModeEventsFor example, when the user instructs GenealogyExample to switch from showing ancestors to showing descendants, the tree model makes the change and then fires an event to inform its listeners (such as the tree component).例如,当用户指示GenealogyExample从显示祖先切换到显示后代时,树模型进行更改,然后触发事件通知其监听器(如树组件)。

How to Load Children Lazily如何让子节点懒加载

Lazy loading is a characteristic of an application when the actual loading and instantiation of a class is delayed until the point just before the instance is actually used.延迟加载是应用程序的一个特征,当类的实际加载和实例化延迟到实例实际使用之前。

Do we gain anything by loading them lazily? 我们通过懒洋洋地加载它们获得了什么吗?Yes, this would definitely add to the performance of an application. 是的,这肯定会提高应用程序的性能。By lazily loading, you can dedicate the memory resources to load and instantiate an object only when it is actually used. 通过延迟加载,您可以将内存资源专用于仅在实际使用时加载和实例化对象。You can also speed up the initial loading time of an application.您还可以加快应用程序的初始加载时间。

One of the ways you can lazily load children of a Tree is by utilizing the TreeWillExpandListener interface. 使用TreeWireExpandListener接口可以惰性地加载树的子级。For example, you can declare and load root, grandparent and parent of a Tree along with the application as shown in the following code:例如,您可以声明和加载树的根、祖父母和父级以及应用程序,如以下代码所示:

Let us declare the root, grandparent and parent as shown below:让我们声明根、祖父母和父母,如下所示:

class DemoArea extends JScrollPane
                   implements TreeWillExpandListener {
        .......
        .......

        private TreeNode createNodes() {
            DefaultMutableTreeNode root;
            DefaultMutableTreeNode grandparent;
            DefaultMutableTreeNode parent;

            root = new DefaultMutableTreeNode("San Francisco");

            grandparent = new DefaultMutableTreeNode("Potrero Hill");
            root.add(grandparent);

            parent = new DefaultMutableTreeNode("Restaurants");
            grandparent.add(parent);
            
            dummyParent = parent;
            return root;
        }

You can load above declared nodes to the tree as shown in the following code:您可以将上述声明的节点加载到树中,如以下代码所示:

TreeNode rootNode = createNodes();
tree = new JTree(rootNode);
tree.addTreeExpansionListener(this);
tree.addTreeWillExpandListener(this);
.......
.......
setViewportView(tree);

Now, you can load children lazily to the application whenever the parent node Restaurants is visible in the application. 现在,只要父节点Restaurants在应用程序中可见,就可以将子节点惰性加载到应用程序中。To do this, let us declare two children in a separate method and call that method as shown in the following code:为此,让我们在一个单独的方法中声明两个子对象,并调用该方法,如下代码所示:

private void LoadLazyChildren(){
            DefaultMutableTreeNode child;
            child = new DefaultMutableTreeNode("Thai Barbeque");
            dummyParent.add(child);
            child = new DefaultMutableTreeNode("Goat Hill Pizza");
            dummyParent.add(child);
            textArea.append(" Thai Barbeque and Goat Hill Pizza are loaded lazily");
        }

        .......
        .......

public void treeWillExpand(TreeExpansionEvent e) 
                    throws ExpandVetoException {
            saySomething("You are about to expand node ", e);
            int n = JOptionPane.showOptionDialog(
                this, willExpandText, willExpandTitle,
                JOptionPane.YES_NO_OPTION,
                JOptionPane.QUESTION_MESSAGE,
                null,
                willExpandOptions,
                willExpandOptions[1]);
           
        LoadLazyChildren();
        }

See How to Write a Tree-Will-Expand Listener for a description of Tree-Will-Expand listeners.请参阅如何编写树将展开侦听器以了解树将展开的侦听器的描述。

The Tree API树API

The tree API is quite extensive. The following tables list just a bit of the API, concentrating on the following categories:树API相当广泛。下表仅列出了API的一部分,集中于以下类别:

For more information about the tree API, see the API documentation for JTree and for the various classes and interfaces in the tree package. 有关树API的更多信息,请参阅JTree的API文档以及树包中的各种类和接口。Also refer to The JComponent Class for information on the API JTree inherits from its superclass.有关JTree从其超类继承的API的信息,请参阅JComponent类

Tree-Related Classes and Interfaces树相关类和接口
Class or Interface类或接口 Purpose目的
JTree The component that presents the tree to the user.向用户呈现树的组件。
TreePath Represents a path to a node.表示节点的路径。
TreeNode
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 Getting Started.有关更多信息,请参阅入门
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. TreeWireExpandListener可以抛出的异常,以指示即将发生的扩展/崩溃不应发生。For more information, see How to Write a Tree-Will-Expand Listener.有关更多信息,请参阅如何编写树将展开侦听器
Creating and Setting Up a Tree创建和设置树
Constructor or 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. TreeNode参数指定要由默认树模型管理的根节点。The TreeModel argument specifies the model that provides the data to the table. TreeModel参数指定为表提供数据的模型。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, or 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. 如果指定Hashtable、对象数组或Vector作为参数,则该参数将被视为根节点(未显示)下的节点列表,并相应地构造模型和树节点。

The boolean argument, if present, specifies how the tree should determine whether a node should be displayed as a leaf. boolean参数(如果存在)指定树应如何确定节点是否应显示为叶。If the argument is false (the default), any node without children is displayed as a leaf. 如果参数为false(默认值),则任何没有子节点的节点都显示为叶。If the argument is true, a node is a leaf only if its getAllowsChildren method returns false.如果参数为true,则只有当节点的getAllowsChildren方法返回false时,节点才是叶。

void setCellRenderer(TreeCellRenderer) Set 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 setRootVisible(boolean) Set whether the tree shows the root node. 设置树是否显示根节点。The default value is false if the tree is created using one of the constructors that takes a data structure, and true otherwise.如果使用采用数据结构的构造函数之一创建树,则默认值为false,否则为true
void setShowsRootHandles(boolean) Set whether the tree shows handles for its leftmost nodes, letting you expand and collapse the nodes. 设置树是否显示其最左侧节点的控制柄,以便展开和折叠节点。The default is false. 默认值为falseIf the tree does not show the root node, then you should invoke setShowsRootHandles(true).如果树不显示根节点,则应调用setShowsRootHandles(true)
void setDragEnabled(boolean)
boolean getDragEnabled()
Set or get the dragEnabled property, which must be true to enable drag handling on this component. 设置或获取dragEnabled属性,该属性必须为true才能在此组件上启用拖动处理。The default value is false. 默认值为falseSee Drag and Drop and Data Transfer for more details.有关详细信息,请参阅拖放和数据传输
Implementing Selection实施选择
Method方法 Purpose目的
void addTreeSelectionListener(TreeSelectionListener) Register a listener to detect when the a node is selected or deselected.注册侦听器以检测何时选择或取消选择a节点。
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).可以使用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).该值可以是CONTIGUOUS_TREE_SELECTIONDISCONTIGUOUS_TREE_SELECTIONSINGLE_TREE_SELECTION(均在TreeSelectionModel中定义)。
Object getLastSelectedPathComponent() Get the object representing the currently selected node. 获取表示当前选定节点的对象。This is equivalent to invoking getLastPathComponent on the value returned by tree.getSelectionPath().这相当于对tree.getSelectionPath()返回的值调用getLastPathComponent
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.要否决即将发生的扩展或崩溃,TreeWireExpandListener可以抛出ExpandVetoException
void expandPath(TreePath)
void collapsePath(TreePath)
Expand or collapse the specified tree path.展开或折叠指定的树路径。
void scrollPathToVisible(TreePath) Ensure 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) Ensure 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.默认值为true
void setToggleClickCount(int)
int getToggleClickCount()
Set or get the number of mouse clicks before a node will expand or close. 设置或获取节点展开或关闭之前的鼠标单击次数。The default is two.默认值为2。
TreePath getNextMatch(String, int, Position.Bias) Return the TreePath to the next tree element that begins with the specific prefix.TreePath返回到以特定前缀开头的下一个树元素。

Examples that Use Trees使用树的示例

This table lists examples that use JTree and where those examples are described.下表列出了使用JTree的示例以及这些示例的描述位置。

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 and feel.创建响应用户选择的树。它还包含用于定制Java外观的线条样式的代码。
TreeIconDemo Customizing a Tree's Display自定义树的显示 Adds a custom leaf icon to TreeDemo.将自定义叶图标添加到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.还允许编辑节点文本。
GenealogyExample Creating a Data Model创建数据模型 Implements a custom tree model and custom node type.实现自定义树模型和自定义节点类型。
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.显示如何否决节点扩展。

If you are programming in JavaFX, see Tree View.如果您使用JavaFX编程,请参阅树视图


Previous page: How to Use Tool Tips
Next page: How to Use HTML in Swing Components