View RSS Feed

Java Swing

How to use JTree in Java

Rate this Entry
by , 02-29-2012 at 06:14 AM (12394 Views)
JTree is a Swing component that displays hierarchical data in a tree structure. The screen-shot below depicts a tree component:

Name:  sample tree.png
Views: 6926
Size:  12.3 KB
Figure: A tree component in Java

This article will focus on how to implement such a tree component in Java, by walking through basic concepts to develop an interesting sample application.

Fundamental concepts about Tree component

  • Node: A tree displays data vertically in rows, each row contains only one item or node.
  • Root node: A tree has one root node, all other nodes descend from this node. Thereís only one root node per tree.
  • Branch node: is a node that has children nodes, like a branch of a tree.
  • Leaf node: is a node that has no children, like a leaf in a branch of a tree.



The screen-shot above illustrates the concepts visually.

The Tree API

The Java platform provides extensive classes and interfaces for tree-related programming. Beside the JTree class in the javax.swing package, almost others reside in a separate package: javax.swing.tree.

Here are some noticeable classes and interfaces:

  • JTree: The component class that displays the tree.
  • TreeNode: This interface specifies the requirements that an object should implement to be used as a tree node. Java provides a default implementation class DefaultMutableTreeNode.
  • TreeModel: This interface defines the data model that is used by the JTree. The JTree displays the tree structure based on the model. The DefaultTreeModel class is the default implementation provided by the Java platform.
  • TreeCellRenderer: An object must implement this interface to customize the display of a tree node. DefaultTreeCellRenderer is the default implementation.
  • TreeCellEditor: An object must implement this interface to handle edit action on a tree node.
  • TreeSelectionListener: Implement this interface to listen to selection changes on the tree.


The Tree demo project

We are going to develop a simple Swing application to demonstrate the usage of JTree in Java. The program looks like this:

Name:  sample app.png
Views: 6073
Size:  24.0 KB
Figure: A sample application using JTree

As you can see in the sample application, the tree looks like a Java project tree which contains source folder (src), test folder, packages and Java classes. If a node is clicked, a message dialog appears to indicate that the node has been selected:

Name:  message dialog.png
Views: 5783
Size:  20.4 KB
Figure: The message dialog appears when selecting a node in the tree


Defining the node class

We should define a class that represents a node in the tree. This class should implement the interface TreeNode and provide implementation for the methods defined by that interface. A node object has a title (which is displayed in the tree), a collection holds children nodes, a reference to parent node. Here is the source code of the class, ProjectItemNode.java:

Java Code: Definition of a node
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.tree.TreeNode;

/**
* Defines a tree node
* @author Ha Minh Nam
*
*/
public class ProjectItemNode implements TreeNode {
    /**
     * The title will be displayed in the tree
     */
    private String title;
   
    /*
     * Type of this node, which is used by a renderer to set appropriate icon
     * for the node
     */
    private int type;

    private Vector<TreeNode> children = new Vector<TreeNode>();
    private TreeNode parent;
   
    // Constants for types of node
    public static final int NODE_ROOT         = 0;
    public static final int NODE_PROJECT     = 1;
    public static final int NODE_SOURCE          = 2;
    public static final int NODE_PACKAGE      = 4;
    public static final int NODE_CLASS          = 5;
    public static final int NODE_FOLDER      = 6;
   
    public ProjectItemNode(String title, int type) {
        this.title = title;
        this.type = type;
    }
   
    public void addChild(TreeNode child) {
        children.add(child);
    }
   
    public void setParent(TreeNode parent) {
        this.parent = parent;
    }
   
    @Override
    public Enumeration<TreeNode> children() {
        return children.elements();
    }

    @Override
    public boolean getAllowsChildren() {
        return true;
    }

    @Override
    public TreeNode getChildAt(int childIndex) {
        return children.elementAt(childIndex);
    }

    @Override
    public int getChildCount() {
        return children.size();
    }

    @Override
    public int getIndex(TreeNode node) {
        return children.indexOf(node);
    }

    @Override
    public TreeNode getParent() {
        return this.parent;
    }

    @Override
    public boolean isLeaf() {
        return (children.size() == 0);
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
   
    /**
     * The node object should override this method to provide a text that will
     * be displayed for the node in the tree.
     */
    public String toString() {
        return title;
    }

    public int getType() {
        return type;
    }   
}
Some notices about the class ProjectItemNode.java:

  • The attribute type is to determine which type that the node to be of: root, project, source, package, class, and folder. This type then will be used by a renderer class to set appropriate icon for the node.
  • It implements the following methods defined by the TreeNode interface: children(), getAllowsChildren(), getChildAt(), getChildCount(), getIndex(), getParent(), and isLeaf().



Although itís not mandatory to implement the TreeNode interface, the TreeNode interface defines common requirements that a node class should follow, and implementing that interface is preferred.


Creating tree model

We have defined the node class. Next, we need to define a model class by implementing the TreeModel interface. The JTree class has a constructor that accepts a TreeModel object which is used to draw the hierarchical structure of the tree. Here is the source code, the ProjectTreeModel.java class:

Java Code: The tree model class
import java.util.Vector;

import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

/**
* Defines the data model used by the JTree class
* @author Ha Minh Nam
*
*/
public class ProjectTreeModel implements TreeModel {

    private TreeNode rootNode;
   private Vector<TreeModelListener> listeners =
       new Vector<TreeModelListener>();
   
    public ProjectTreeModel(TreeNode rootNode) {
        this.rootNode = rootNode;
    }
   
    @Override
    public void addTreeModelListener(TreeModelListener l) {
        listeners.add(l);
    }

    @Override
    public Object getChild(Object parent, int index) {
        TreeNode parentNode = (TreeNode) parent;
        return parentNode.getChildAt(index);
    }

    @Override
    public int getChildCount(Object parent) {
        TreeNode parentNode = (TreeNode) parent;
        return parentNode.getChildCount();
    }

    @Override
    public int getIndexOfChild(Object parent, Object child) {
        TreeNode parentNode = (TreeNode) parent;
        TreeNode childNode = (TreeNode) child;
        return parentNode.getIndex(childNode);
    }

    @Override
    public Object getRoot() {
        return rootNode;
    }

    @Override
    public boolean isLeaf(Object node) {
        TreeNode treeNode = (TreeNode) node;
        return treeNode.isLeaf();
    }

    @Override
    public void removeTreeModelListener(TreeModelListener l) {
        listeners.remove(l);
    }

    @Override
    public void valueForPathChanged(TreePath path, Object newValue) {
    }

}

Some noteworthy points about the implementation of this class:
  • It defines the root of the tree by the variable rootNode and the accessor getRoot().
  • It implements the methods defined by the TreeModel interface to get the parent node, children nodes of a specific node. Those methods are important because they decide the hierarchical structure of the tree through the parent-child relationship among nodes. So pay attention to implement those methods properly.
  • It also adding and removing listeners of the TreeModelListener interface. The listeners will be notified if the model data has changed. But we donít implement the notification in the model class.



Implementing the node renderer

Next, we need to extend the DefaultTreeCellRender class in order to customize the look and feel of a tree node based on type of the node. For example a project node will have project icon, a package node will look like a package, a Java class node will have its own icon Ö Like you see in the screen-shot of the application in the previous section. Here is the source code of the class NodeRenderer.java:

Java Code: The renderer class
import java.awt.Component;

import javax.swing.ImageIcon;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;

/**
* This class is implemented to customize the display of a node.
* @author Ha Minh Nam
*
*/
public class NodeRenderer extends DefaultTreeCellRenderer {
   private ImageIcon iconProject
       = new ImageIcon(getClass().getResource("/hainasoft/codelib/desktop/tree/project.png"));
   private ImageIcon iconSource
       = new ImageIcon(getClass().getResource("/hainasoft/codelib/desktop/tree/source.png"));
   private ImageIcon iconPackage
       = new ImageIcon(getClass().getResource("/hainasoft/codelib/desktop/tree/package.png"));
   private ImageIcon iconClass
       = new ImageIcon(getClass().getResource("/hainasoft/codelib/desktop/tree/class.png"));
   private ImageIcon iconFolder
       = new ImageIcon(getClass().getResource("/hainasoft/codelib/desktop/tree/folder.png"));
   
   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);
      
       ProjectItemNode node = (ProjectItemNode) value;
      
       switch (node.getType()) {
           case ProjectItemNode.NODE_PROJECT:
               setIcon(iconProject);
               break;
           case ProjectItemNode.NODE_SOURCE:
               setIcon(iconSource);
               break;
           case ProjectItemNode.NODE_PACKAGE:
               setIcon(iconPackage);
               break;
           case ProjectItemNode.NODE_CLASS:
               setIcon(iconClass);
               break;
           case ProjectItemNode.NODE_FOLDER:
               setIcon(iconFolder);
               break;
       }
      
       return this;
   }
}
As you can see in the source code of the NodeRenderer.java class, you should have 5 image icons available in the source package: project, source, package, class, folder.

The render class implements only one method: getTreeCellRendererComponent(), in this method, the type of the current node is determine and the appropriate icon is set.


Writing a tree builder

So far we have implemented the node class, the model class and the renderer class. However the tree structure has not been defined yet. Thus we will write a separate class with a static method that builds the tree structure. Here is the source code of the ProjectTreeBuilder.java class:


Java Code: The builder class
public class ProjectTreeBuilder {
    public static ProjectItemNode build() {
        ProjectItemNode rootNode = new ProjectItemNode("Root", ProjectItemNode.NODE_ROOT);
       
        ProjectItemNode projectNode = new ProjectItemNode("TreeApp", ProjectItemNode.NODE_PROJECT);
       
        ProjectItemNode sourceNode = new ProjectItemNode("src", ProjectItemNode.NODE_SOURCE);
        ProjectItemNode srcPackageNode = new ProjectItemNode("com.mycompany.tree", ProjectItemNode.NODE_PACKAGE);
        ProjectItemNode srcClassNode1 = new ProjectItemNode("TreeApp.java", ProjectItemNode.NODE_CLASS);
        ProjectItemNode srcClassNode2 = new ProjectItemNode("ProjectItemNode.java", ProjectItemNode.NODE_CLASS);
        ProjectItemNode srcClassNode3 = new ProjectItemNode("ProjectTreeModel.java", ProjectItemNode.NODE_CLASS);
        ProjectItemNode srcClassNode4 = new ProjectItemNode("ProjectTreeBuilder.java", ProjectItemNode.NODE_CLASS);
        ProjectItemNode srcClassNode5 = new ProjectItemNode("NodeRenderer.java", ProjectItemNode.NODE_CLASS);
       
        srcPackageNode.addChild(srcClassNode1);
        srcPackageNode.addChild(srcClassNode2);
        srcPackageNode.addChild(srcClassNode3);
        srcPackageNode.addChild(srcClassNode4);
        srcPackageNode.addChild(srcClassNode5);
        sourceNode.addChild(srcPackageNode);
               
        projectNode.addChild(sourceNode);

        ProjectItemNode testNode = new ProjectItemNode("test", ProjectItemNode.NODE_SOURCE);
        ProjectItemNode testPackageNode = new ProjectItemNode("com.mycompany.test", ProjectItemNode.NODE_PACKAGE);
        ProjectItemNode testClassNode = new ProjectItemNode("TreeAppTest.java", ProjectItemNode.NODE_CLASS);
       
        testPackageNode.addChild(testClassNode);       
        testNode.addChild(testPackageNode);
       
        projectNode.addChild(testNode);

        projectNode.addChild(new ProjectItemNode("bin", ProjectItemNode.NODE_FOLDER));

        projectNode.addChild(new ProjectItemNode("lib", ProjectItemNode.NODE_FOLDER));
       
        rootNode.addChild(projectNode);
       
       
        return rootNode;
    }
}
The static method build() returns a ProjectItemNode object which is the root node of the tree. From this root node, we added project node, source nodes, package nodes, class nodesÖ to make the structure like the screen-shot above.


Wiring the pieces together

Now itís time to wire all the pieces together by a program that extends a JFrame. Here is the source code of the TreeApp.java class:

Java Code: The application class
import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeModel;

/**
* The program demonstrates JTree implementation.
* @author Ha Minh Nam
*
*/
public class TreeApp extends JFrame implements TreeSelectionListener {
   
    private JTree tree;
   
    public TreeApp() {
        super("Tree Demo Application");
        setLayout(new BorderLayout());
        ProjectItemNode rootNode = ProjectTreeBuilder.build();
        TreeModel model = new ProjectTreeModel(rootNode);
        tree = new JTree(model);
        tree.setCellRenderer(new NodeRenderer());
        tree.addTreeSelectionListener(this);
        add(new JScrollPane(tree), BorderLayout.CENTER);
       
        // expand all nodes in the tree to be visible
        for (int i = 0; i < tree.getRowCount(); i++) {
            tree.expandRow(i);
        }
       
        setSize(280, 300);
       
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }
   
    public static void main(String[] args) {
        // set system look and feel
       try {
           UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       } catch (Exception e) { }
       
        SwingUtilities.invokeLater(new Runnable() {
           
            @Override
            public void run() {
                new TreeApp().setVisible(true);
            }
        });
    }

    @Override
    public void valueChanged(TreeSelectionEvent e) {
       Object node = tree.getLastSelectedPathComponent();
       if (node == null) {
           return;
       }
       
       JOptionPane.showMessageDialog(this, "You have selected: " + node);
    }
}
All the code that makes up the GUI is written in the constructor. First, the tree structure is built by calling ProjectTreeBuilder.build(), the returned root node is then passed to the constructor of the model class. The tree is created with the model class passed as a constructor argument. Next the renderer is set to the tree by the method setCellRenderer(), and finally the tree is wrapped by a JScrollPane before adding to the frame.

The TreeApp.java class also implements the TreeSelectionListener interface to handle selection event on a tree node. The method valueChanged() has to be implemented and is invoked when a node is selected.

Finally, the main() method launches the application.


Conclusion

So far you have been familiar with JTree in Java. Remember the following steps: Define tree node class; Create model class; Create renderer class; Write builder class; and wiring all together by a main program.

Submit "How to use JTree in Java" to Facebook Submit "How to use JTree in Java" to Digg Submit "How to use JTree in Java" to del.icio.us Submit "How to use JTree in Java" to StumbleUpon Submit "How to use JTree in Java" to Google

Tags: java, jtree, swing Add / Edit Tags
Categories
Tree

Comments