简体   繁体   English

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

[英]How to insert and display new node in a JTree of the file system contents

I have successfully created a small program that will display the file system contents in a JTree. 我已经成功创建了一个小程序,该程序将在JTree中显示文件系统内容。 It shows only folders and plain text files by design. 它仅显示文件夹和纯文本文件。 I would now like to add a folder to the tree when it is created. 我现在想在创建树时将文件夹添加到树中。 The example program will display the tree and create a folder, but it will not insert a new node for the folder that was just created. 该示例程序将显示树并创建一个文件夹,但是不会为刚刚创建的文件夹插入新节点。 How can I insert a node into the tree when a folder is created and have the tree display the new folder? 创建文件夹后如何在树中插入节点并使树显示新文件夹?

Many thanks! 非常感谢!

This class creates and displays the tree. 此类创建并显示树。

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);
    }

}

This class is the tree model. 此类是树模型。

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;
            }
        }

}

This last class is the tree cell renderer. 最后一个类是树单元渲染器。

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;
        }


}

There seems to be a basic mismatch of approaches. 方法似乎基本不匹配。 You are using DefaultMutableTreeNode s, which cache there child nodes, but are managing the actual nodes directly via the model, this is causing confusion at multiple different levels... 您正在使用DefaultMutableTreeNode ,该缓存将子节点缓存在其中,但是直接通过模型管理实际节点,这在多个不同级别上造成了混乱。

If you want to continue monitoring the disk contents directly (or proxying it), I would create a custom TreeNode whose sole responsibility was to monitor a single directory. 如果要继续直接监视磁盘内容(或代理它),我将创建一个自定义TreeNode其唯一职责是监视单个目录。

I would then create a custom TreeModel (probably from the DefaultTreeModel ), which provided a makeDirectory method, which you would pass the currently selected TreeNode and the name of the directory. 然后,我将创建一个自定义TreeModel (可能来自DefaultTreeModel ),该模型提供了makeDirectory方法,该方法将传递当前选择的TreeNode和目录名称。

This method would then be responsible for the physical creation of the directory and notification of the structural changes to the JTree (via the nodesWereInserted method). 然后,此方法将负责目录的物理创建以及JTree的结构更改通知(通过nodesWereInserted方法)。 I'd probably have this node return an instance a TreeNode which represented the child... 我可能会让这个节点返回一个代表子节点的TreeNode实例。

There are a lot of problems with this approach, namly with the referencing of objects. 这种方法存在很多问题,尤其是在引用对象方面。 In your current approach, you are creating a new DefaultMutableTreeNode whenever getChild is called, this could cause issues if parts of the API are relying on those references remaining constant for the given position/data, this would require you to maintain some kind of internal cache, linking the File with the TreeNode ...which kind of defeats the purpose... 在当前方法中,每当调用getChild时,您都将创建一个新的DefaultMutableTreeNode ,如果API的某些部分依赖那些对给定位置/数据保持不变的引用,则可能会导致问题,这将需要您维护某种内部缓存,将FileTreeNode链接...这有损于目标...

A better approach might be to utilising the existing "mutable tree node" API, each node would still be responsible for a single File , but it would also cache the results. 更好的方法可能是利用现有的“可变树节点” API,每个节点仍将负责单个File ,但也将缓存结果。 The problem here is managing when a node should be populated (as you don't want to populate directory nodes that are not expanded). 这里的问题是管理何时应填充节点(因为您不想填充未扩展的目录节点)。

I you are planning on utilising the "Watch Service" API, then I would go for the "mutable tree node" API and cache the child files within it (as you need to), it will simply the issues... 我打算使用“监视服务” API,然后使用“可变树节点” API并在其中缓存子文件(根据需要),这将只是问题...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM