2012/12/02

Tree of files in JTree

In fact, what this article will say has nothing to do with the components in TWaver just because I find it quite interesting. Therefore, I would like to share it with you. The file tree is completely based on JTree of swing. Now let's first see the final effect:

Screenshot:



Introduction on Functions:
  • to show the structure of files in tree
  • the icon of files should be systematic ones
  • The background color of the current node will be changed when you mouse over it.(such as the bricky-red background of the text "Windows" in the image above)
First let' s see the class structure:
  • main program
  • file tree, which inherits from JTree
  • the encapsulated node, including the name and the icon of a file, the File class and other identity
  • The customized node renderer which inherits from DefaultTreeCellRenderer
  • the customized TreeModel which inherits from DefaultTreeModel
Considering that there will be quite a few system files, it is not reasonable to initialize the whole tree at starting a program. So we have taken measures to delay loading: to only initialize the child nodes of a node only when it is to be expanded. Now add a listener into the construction of FileTree:
addTreeWillExpandListener(new TreeWillExpandListener() {
            @Override
            public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
                DefaultMutableTreeNode lastTreeNode =
(DefaultMutableTreeNode) event.getPath().getLastPathComponent();
                FileNode fileNode = (FileNode) lastTreeNode.getUserObject();
                if (!fileNode.isInit) {
                    File[] files;
                    if (fileNode.isDummyRoot) {
                        files = fileSystemView.getRoots();
                    } else {
                        files = fileSystemView.getFiles(
                                ((FileNode) lastTreeNode.getUserObject()).file,
                                false);
                    }
                    for (int i = 0; i < files.length; i++) {
                       //The name and the icon of the file is obtained through fileSystemView.
                        FileNode childFileNode = new FileNode(
                                fileSystemView.getSystemDisplayName(files[i]),
                                fileSystemView.getSystemIcon(files[i]), files[i],
                                false);
                        DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(childFileNode);
                        lastTreeNode.add(childTreeNode);
                    }
                    //To notify that a node has been changed
                    DefaultTreeModel treeModel1 = (DefaultTreeModel) getModel();
                    treeModel1.nodeStructureChanged(lastTreeNode);
                }
                //to change the identifier to avoid loading repeatedly
                fileNode.isInit = true;
            }
            @Override
            public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {

            }
        });
Of course, this method must be combined with TableModel. I have reloaded DefaultTreeModel and initialized the root node in the construction. Then load the method isLeaf.

Now let's think how to change the background color of a node when you mouse over it.

Right at this time I recalled the mistake I made during the time when I first learned Renderer that every node has a Renderer and I even tend to add a listener to it! The thing I have to emphasize is that Renderer is just a renderer. JTree will call it to render nodes onto the screen to show them. But remember however nodes there are, there is only one Renderer in a JTree!

Since it is useless to add a listener to Renderer, we have to change our focus, that is, to add listener which listens to mouse-moves in JTree and then repaint the node on which the mouse is.

addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
//to obtain the TreePath of the mouse
                TreePath path=getPathForLocation(e.getX(), e.getY());

//to calculate the area to be reprinted and to repaint JTree
                if(path!=null){
                    if(mouseInPath!=null){
                        Rectangle oldRect=getPathBounds(mouseInPath);
                        mouseInPath=path;
                        repaint(getPathBounds(path).union(oldRect));
                    }else{
                        mouseInPath=path;
                        Rectangle bounds=getPathBounds(mouseInPath);
                        repaint(bounds);
                    }
                }else if(mouseInPath!=null){
                    Rectangle oldRect=getPathBounds(mouseInPath);
                    mouseInPath=null;
                    repaint(oldRect);
                }
            }
        });
The background color of mouseInPath can be changed in Renderer only when the TreePath(mouseInPath) of MouseOver is saved in JTree.
FileTree fileTree=(FileTree)tree;
        JLabel label= (JLabel) super.getTreeCellRendererComponent(tree,value,sel,expanded,leaf,row,hasFocus);

        DefaultMutableTreeNode node=(DefaultMutableTreeNode)value;
        FileNode fileNode=(FileNode)node.getUserObject();
        label.setText(fileNode.name);
        label.setIcon(fileNode.icon);

        label.setOpaque(false);
//to change the background color if the node in rendering now is the node under the mouse over
        if(fileTree.mouseInPath!=null&&
                fileTree.mouseInPath.getLastPathComponent().equals(value)){
            label.setOpaque(true);
            label.setBackground(new Color(255,0,0,90));
        }
        return label;
Now it is the end of the article. If you have interest in it, welcome for your communication and good ideas.

No comments:

Post a Comment