Как вставить и отобразить новый узел в JTree содержимого файловой системы

Я успешно создал небольшую программу, которая будет отображать содержимое файловой системы в 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 ответ

Решение

Кажется, есть основное несоответствие подходов. Ты используешь DefaultMutableTreeNodes, которые кэшируют там дочерние узлы, но управляют фактическими узлами напрямую через модель, это вызывает путаницу на множестве разных уровней...

Если вы хотите продолжить мониторинг содержимого диска напрямую (или прокси), я бы создал пользовательский TreeNode чья единственная ответственность заключалась в мониторинге одного каталога.

Я бы тогда создал кастом TreeModel (вероятно из DefaultTreeModel), который обеспечил makeDirectory метод, который вы передадите выбранному в данный момент TreeNode и название каталога.

Этот метод затем будет отвечать за физическое создание каталога и уведомление о структурных изменениях в JTree (через nodesWereInserted метод). Я бы, вероятно, чтобы этот узел возвращал экземпляр TreeNode который представлял ребенка...

Есть много проблем с этим подходом, в частности, со ссылками на объекты. В вашем текущем подходе вы создаете новый DefaultMutableTreeNode всякий раз, когда getChild вызывается, это может вызвать проблемы, если части API полагаются на эти ссылки, оставаясь неизменными для данной позиции / данных, это потребует от вас поддерживать некоторый вид внутреннего кеша, связывая File с TreeNode... какой вид поражения цели...

Лучшим подходом может быть использование существующего API "изменяемого узла дерева", каждый узел по-прежнему будет отвечать за один File, но это также кеширует результаты. Проблема здесь заключается в управлении, когда узел должен быть заполнен (так как вы не хотите заполнять узлы каталога, которые не развернуты).

Если вы планируете использовать API "Watch Service", то я бы выбрал API "mutable tree node" и кешировал дочерние файлы внутри него (как вам нужно), это просто вызовет проблемы...

Другие вопросы по тегам