如何在文件系统内容的JTree中插入和显示新节点

时间:2014-12-07 23:47:10

标签: java swing jtree defaulttreemodel

我已经成功创建了一个小程序,它将在JTree中显示文件系统内容。它仅按设计显示文件夹和纯文本文件。我现在想在创建树时向树中添加一个文件夹。示例程序将显示树并创建一个文件夹,但不会为刚刚创建的文件夹插入新节点。如何在创建文件夹时将节点插入树中并使树显示新文件夹?

非常感谢!

此类创建并显示树。

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

public class TreeFrame extends JFrame{

    private JTree fileTree = null;
    private String USER_HOME = System.getProperty("user.home");

    public TreeFrame(){
        super("File Tree");
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        //Create tree
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(new File(USER_HOME));
        fileTree = new JTree(new FileTreeModelTest(rootNode));
        fileTree.setCellRenderer(new TreeCellRendererTest());
        fileTree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                //Commented out the next part because it causes a null pointer exception.
                //I believe it is caused by the tree not inserting a node.
                //JTree tree = (JTree)e.getSource();
                //DefaultMutableTreeNode node = (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
                //Object obj = node.getUserObject();//Causes a null pointer exception
                //if(obj instanceof File){
                //    File f = (File)node.getUserObject();
                //    System.out.println(f.getAbsolutePath());
                //}
            }
        });

        JPanel panel = new JPanel();
        JLabel folderLabel = new JLabel("Name:");
        panel.add(folderLabel, BorderLayout.WEST);
        final JTextField textField = new JTextField(25);
        textField.setPreferredSize(new Dimension(100, 28));
        panel.add(textField, BorderLayout.CENTER);
        JButton button = new JButton("Create Foder");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                //Add a folder when the menu item is clicked
                DefaultMutableTreeNode node = (DefaultMutableTreeNode)fileTree.getLastSelectedPathComponent();
                File f = (File)node.getUserObject();
                if(f.isDirectory()){
                    File dir = new File(f.getAbsolutePath()+"/"+textField.getText().trim());

                    if(dir.mkdirs()){
                        System.out.println("ADDED: " + dir.getAbsolutePath());

                        //Insert node into tree model -- this is the part that does not seem to work.
                        FileTreeModelTest model = (FileTreeModelTest)fileTree.getModel();
                        DefaultMutableTreeNode child = new DefaultMutableTreeNode(dir);
                        model.insertNodeInto(child, node, node.getChildCount());
                        model.reload(node);
                        TreeNode[] nodes = model.getPathToRoot(child);
                        TreePath path = new TreePath(nodes);
                        fileTree.scrollPathToVisible(path);
                        fileTree.setSelectionPath(path);

                    }

                }
            }
        });
        panel.add(button, BorderLayout.EAST);
        getContentPane().add(panel, BorderLayout.NORTH);

        JScrollPane scrollPane = new JScrollPane(fileTree);
        scrollPane.setPreferredSize(new Dimension(200, 400));

        getContentPane().add(scrollPane, BorderLayout.CENTER);
        pack();

    }

    public static void main(String[] args){
        TreeFrame frame = new TreeFrame();
        frame.setVisible(true);
    }

}

这个类是树模型。

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;

public class FileTreeModelTest extends DefaultTreeModel {

    public FileTreeModelTest(DefaultMutableTreeNode root){
            super(root);
        }

        private File[] getFiles(File parent){
            File[] f = parent.listFiles(new FileFilter() {
                @Override
                public boolean accept(File file) {
                    if(file==null)
                        return false;
                    if(file.isDirectory())
                        return true;
                    if(file.getName().toLowerCase().endsWith("txt")){
                        return true;
                    }
                    return false;
                }
            });
            return f;
        }

        @Override
        public Object getChild(Object parent, int index){
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent;
            File p = (File)node.getUserObject();
            File[] pFile = getFiles(p);
            DefaultMutableTreeNode child = new DefaultMutableTreeNode(pFile[index]);
            return child;
        }

        @Override
        public int getChildCount(Object parent){
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent;
            File f = (File)node.getUserObject();
            if(!f.isDirectory()){
                return 0;
            }else{
                //Is a directory, return number of folders and text files
                File[] children = getFiles(f);
                if(children==null) return 0;
                return children.length;
            }
        }

        @Override
        public int getIndexOfChild(Object parent, Object child){
            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)parent;
            DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)child;
            File p = (File)parentNode.getUserObject();
            File c = (File)childNode.getUserObject();

            File[] pFile = getFiles(p);
            return Arrays.asList(pFile).indexOf(c);
        }

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

        @Override
        public boolean isLeaf(Object parent){
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent;
            File f = (File)node.getUserObject();
            if(f.isDirectory()){
                File[] children = getFiles(f);
                if(children==null) return true;
                //F is a directory. If it has no files, then it is a leaf.
                return children.length==0;
            }else{
                //F is a file. It is a leaf.
                return true;
            }
        }

}

最后一个类是树单元格渲染器。

import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;
import java.io.File;

public class TreeCellRendererTest extends DefaultTreeCellRenderer {

    private FileSystemView fsv = FileSystemView.getFileSystemView();

    public TreeCellRendererTest(){

    }

    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(value instanceof DefaultMutableTreeNode){
                Object obj = ((DefaultMutableTreeNode) value).getUserObject();

                if(obj instanceof File){
                    File f = (File)obj;
                    if(row==0){
                        setIcon(fsv.getSystemIcon(f));
                        setText(f.getPath());
                    }else
                    if(f.isFile()){
                        setIcon(fsv.getSystemIcon(f));
                        setText(f.getName());
                    }else{
                        setIcon(fsv.getSystemIcon(f));
                        setText(f.getName());
                    }
                }
            }

            return this;
        }


}

1 个答案:

答案 0 :(得分:0)

似乎存在基本的不匹配方法。您正在使用DefaultMutableTreeNode来缓存子节点,但是直接通过模型管理实际节点,这会导致多个不同级别的混淆......

如果要继续直接监视磁盘内容(或代理它),我会创建一个自定义TreeNode,其唯一责任是监视单个目录。

然后我会创建一个自定义TreeModel(可能来自DefaultTreeModel),它提供了makeDirectory方法,您可以通过当前选定的TreeNode和名称目录。

然后,此方法将负责物理创建目录并通知JTree的结构更改(通过nodesWereInserted方法)。我可能让这个节点返回一个代表孩子的实例TreeNode ......

这种方法存在很多问题,很难引用对象。在您当前的方法中,无论何时调用DefaultMutableTreeNode,您都会创建一个新的getChild,如果API的某些部分依赖于那些对给定位置/数据保持不变的引用,这可能会导致问题,这将需要你要维护某种内部缓存,将FileTreeNode联系起来......这种目的会失败......

更好的方法可能是利用现有的“可变树节点”API,每个节点仍然负责单个File,但它也会缓存结果。这里的问题是管理何时应该填充节点(因为您不想填充未展开的目录节点)。

我打算使用“Watch Service”API,然后我会选择“可变树节点”API并在其中缓存子文件(根据需要),它只是问题......