java - 在Java中刷新fileSystemViewer

标签 java filesystems jtree

Im正在尝试开发一个使用gui的小型应用程序,以从系统中的任何位置移动文件。我有移动文件的代码,当选中并按下按钮时它们确实会移动,但是我不知道如何刷新文件系统查看器以反映更改。我必须设置系统查看器的代码如下:

public class FileMover  {
//Start of Global Variables
private JTree tree;
private DefaultTreeModel treeModel;
private FileSystemView fileSystemView;
protected File currentFile;
protected LinkedList fileLocations;
protected JTree movedTree;
protected JPanel areaLeft;
protected JPanel areaRight;
protected JPanel areaMiddle;
protected final JFrame openFrame;
//end of global variables.

//Constructor for FileMover
public FileMover()
{
    openFrame = new JFrame("File Mover");
    createFileMover();

}
public void createFileMover(){

    Container contentPane = this.openFrame.getContentPane();
    fileLocations = new LinkedList();


    contentPane.setLayout(new BorderLayout());
    areaLeft = new JPanel();
    areaRight = new JPanel();
    areaMiddle = new JPanel();

    contentPane.add(areaLeft, BorderLayout.WEST);
    contentPane.add(areaRight, BorderLayout.EAST);
    contentPane.add(areaMiddle, BorderLayout.CENTER);


    areaLeft.add(createSystemView());
    movedTree = new JTree(fileLocations.toArray());
    JScrollPane movedPane = new JScrollPane(movedTree);
    JButton moveRightButton = new JButton("->");
    JButton moveLeftButton = new JButton("<-");
    JButton refresh = new JButton("Refresh");

    areaMiddle.setLayout(new GridLayout(1,2));
    areaMiddle.add(moveRightButton);
    areaMiddle.add(refresh);
    areaMiddle.add(moveLeftButton);

    //actualy move the file

    moveRightButton.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e){

            System.out.println("Moving file: "+ currentFile.getName());
            fileLocations.add(currentFile);
            try {
                //move the file to the correct location.
                moveFile(currentFile);


            } catch (IOException ex) {
                Logger.getLogger(FileMover.class.getName()).log(Level.SEVERE, null, ex);
            }
            System.out.println(fileLocations.getFirst().toString());
        }
    });

    //refresh the gui
    refresh.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e){
            refresh();
        }
    });

    //finish setting up the frame
    openFrame.setSize(1280, 768);
    openFrame.setLocationRelativeTo(null);
    openFrame.setDefaultCloseOperation(3);
    openFrame.setResizable(false);
    openFrame.pack();
    openFrame.setVisible(true);


}

/** Add the files that are contained within the directory of this node.
*/
private void showChildren(final DefaultMutableTreeNode node) {
    tree.setEnabled(false);


    SwingWorker<Void, File> worker = new SwingWorker<Void, File>() {
        @Override
        public Void doInBackground() {
            File file = (File) node.getUserObject();
            if (file.isDirectory()) {
                File[] files = fileSystemView.getFiles(file, true); //!!
                if (node.isLeaf()) {
                    for (File child : files) {

                            publish(child);

                    }
                }

            }
            return null;
        }

        @Override
        protected void process(List<File> chunks) {
            for (File child : chunks) {
                node.add(new DefaultMutableTreeNode(child));
            }
        }

        @Override
        protected void done() {

            tree.setEnabled(true);
        }
    };
    worker.execute();
}

/** Update the File details view with the details of this File. */
private void setFileDetails(File file) {
    System.out.println("Path: "+ file.getPath());
    System.out.println("Name: "+ fileSystemView.getSystemDisplayName(file));


}
private void refresh(){
    //refresh the tree here

}
private JScrollPane createSystemView(){
    //file syatem hierarchy
    fileSystemView = FileSystemView.getFileSystemView();    

    // the File tree
        DefaultMutableTreeNode root = new DefaultMutableTreeNode();
        treeModel = new DefaultTreeModel(root);

        TreeSelectionListener treeSelectionListener = new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent tse){
                DefaultMutableTreeNode node =
                    (DefaultMutableTreeNode)tse.getPath().getLastPathComponent();
                showChildren(node);
                setFileDetails((File)node.getUserObject());
                currentFile = (File)node.getUserObject();
            }
        };

        // show the file system roots.
        File[] roots = fileSystemView.getRoots();
        for (File fileSystemRoot : roots) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileSystemRoot);
            root.add( node );
            File[] files = fileSystemView.getFiles(fileSystemRoot, true);
            for (File file : files) {
                if (file.isDirectory()) {
                    node.add(new DefaultMutableTreeNode(file));
                }
            }

        }

        tree = new JTree(treeModel);
        tree.setRootVisible(false);
        tree.addTreeSelectionListener(treeSelectionListener);
        tree.setCellRenderer(new FileTreeCellRenderer());
        tree.expandRow(0);
        JScrollPane treeScroll = new JScrollPane(tree);
        tree.setVisibleRowCount(15);

        Dimension preferredSize = treeScroll.getPreferredSize();
        Dimension widePreferred = new Dimension(
            200,
            (int)preferredSize.getHeight());
        treeScroll.setPreferredSize( widePreferred );

        return treeScroll;
}


左移按钮和右移区域还没有完成,但是我需要的是当我在树中选择一个节点并单击右箭头按钮时,该节点反映的文件/文件夹将由我的moveFile代码在内部移动,并且可以正常工作。但是该更改未反映在树中,因此如何在树中显示此更改,即刷新树以显示文件系统的当前状态?

我已经尝试过treeModel.reload();但这似乎不起作用,并引发空指针异常。

我试过了 :

areaLeft.removeAll();

areaLeft.add(createSystemView());


认为它可以通过重新创建系统视图来刷新它,但这似乎无济于事。

这里的帮助将不胜感激!

编辑:以下是文件树渲染器的请求代码:

/** A TreeCellRenderer for a File. */
class FileTreeCellRenderer extends DefaultTreeCellRenderer {

private static final long serialVersionUID = -7799441088157759804L;

private FileSystemView fileSystemView;

private JLabel label;

FileTreeCellRenderer() {
    label = new JLabel();
    label.setOpaque(true);
    fileSystemView = FileSystemView.getFileSystemView();
}

@Override
public Component getTreeCellRendererComponent(
    JTree tree,
    Object value,
    boolean selected,
    boolean expanded,
    boolean leaf,
    int row,
    boolean hasFocus) {

    DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
    File file = (File)node.getUserObject();
    label.setIcon(fileSystemView.getSystemIcon(file));
    label.setText(fileSystemView.getSystemDisplayName(file));
    label.setToolTipText(file.getPath());

    if (selected) {
        label.setBackground(backgroundSelectionColor);
        label.setForeground(textSelectionColor);
    } else {
        label.setBackground(backgroundNonSelectionColor);
        label.setForeground(textNonSelectionColor);
    }

    return label;
}
}

最佳答案

因为从您的代码看起来您​​知道自己在做什么,所以我仅显示基本示例,该示例仅在您首次按下刷新按钮时才起作用:

private DefaultMutableTreeNode someNode;
private void refresh(){
    System.out.println(someNode);
    treeModel.removeNodeFromParent(someNode);
}


然后像下面这样重写createSystemView()的一部分:

        int  cnt = 0;
        for (File file : files) {
            if (file.isDirectory()) {                    
                if ((cnt++) == 5) {                        //1
                    System.out.println(file.getPath());
                    node.add(someNode = new DefaultMutableTreeNode(file));
                }
                else {
                    node.add(new DefaultMutableTreeNode(file));
                }
            }
        }


仅当您的根目录上至少有六个(注释1)目录时,这才起作用。运行文件,从根目录计算目录-当您按下刷新按钮时,它将删除第六个目录。如果再次按下该按钮,它将尝试删除已经删除的节点,因此您将收到IllegalArgumentException。

您需要在removeNodeFromParent上调用treeModel


传送此消息以从其父节点删除节点。这将消息节点移除,以创建适当的事件。这是删除节点的首选方法,因为它可以为您处理事件创建。


另请参见此example

如果要刷新整个视图,则应该像在初始化时一样在Refersh函数中重新创建模型,或者只是迭代模型并根据需要进行更新-但在这种情况下,我建议对模型进行breadth first traversal设置。

编辑:

“因此,在每次移动后刷新将再次显示文件及其现在应位于的FS”。让我们从重新初始化模型开始,我只表示DefaultTreeModel类的treeModel实例。

因此,在refresh()方法中,您将创建一个新的treeModel实例,并像在createSystemView()方法中一样,用DefaultMutableTreeNode实例填充该实例:

 DefaultMutableTreeNode root = new DefaultMutableTreeNode();
 treeModel = new DefaultTreeModel(root);
 File[] roots = fileSystemView.getRoots();
    for (File fileSystemRoot : roots) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileSystemRoot);
        root.add( node );
        File[] files = fileSystemView.getFiles(fileSystemRoot, true);
        for (File file : files) {
            if (file.isDirectory()) {
                node.add(new DefaultMutableTreeNode(file));
            }
        }
    }


我们已经准备好模型,现在我们需要将其设置到树上,我相信仅仅是:

tree.setModel(treeModel);


应该足够了,请注意,您不需要添加侦听器,因为setModel方法会将侦听器从旧模型重新附加到新模型,并且还通知JTree视图相应地重绘自身-我检查了源代码。如果它不会重绘(我对此表示怀疑,但我尚未测试过),则将其强制在下一行中如下所示:

treeModel.reload();


用于reload()方法here的APIdoc。

但是,如果您想在刷新功能中更新模型(对于执行“刷新”操作,我认为这将是更自然的操作),则需要获取新的第一级目录,然后像这样遍历树(因为我们正在遍历所有根的子代,这将是提到的广度优先遍历):

int firstLevelCount = treeModel.getChildCount(root);
DefaultMutableTreeNode child;
for (int i=0; i < firstLevelCount; i++) {
   child = treeModel.getChild(root, index); 
   // update logic part 1
}
// update logic part 2
treeModel.reload();


您将需要DefaultMutableTreeNode.getUserObject()方法,该方法将返回该树节点表示的文件。

显然,您将要删除模型中所有不在新文件数组中的节点,对于所有在模型中没有对应节点的文件,您都希望将它们添加到模型中。

我建议不要使用文件数组,而是使用列表(以便您可以从List.contains()方法中受益),例如

List<File> files = new ArrayList<File>(Arrays.asList(FileSystemView.getFileSystemView().getRoots()));


另外,像这样重写渲染器的一部分:

if (file != null) {
    label.setIcon(fileSystemView.getSystemIcon(file));
    label.setText(fileSystemView.getSystemDisplayName(file));
    label.setToolTipText(file.getPath());
}


因为视图中的根节点没有关联的文件,所以更新模型时很可能会得到NPE

在我描述的第二个变体中,您可能会很想从循环内部删除根子节点(在更新逻辑1部分)-如果这样做,您很可能会得到ConcurrentModificationException。解决方案是另辟another径

List<DefaultMutableTreeNode> toBeRemoved = new ArrayList<DefaultMutableTreeNode>();


在//更新逻辑第1部分的位置(插入循环),而不是从模型中删除节点,而是将其放在该列表中。完成循环循环后,您只需遍历该列表,然后在// update逻辑第2部分的位置将其从模型中删除即可,例如

for (DefaultMutableTreeNode node : toBeRemoved) {
    treeModel.removeNodeFromParent(node);
}


如前所述,这将自动触发视图(JTree)重绘,因此请参见最适合您的需求。

编辑^ 2:

关于刷新方法的第二种形式,您已经拥有

private DefaultTreeModel treeModel;


作为全局变量,您可以像这样获得树的根(getRoot()

DefaultMutableTreeNode root = (DefaultMutableTreeNode)treeModel.getRoot();


就像您已经做的那样,您将获得文件系统的当前状态,如下所示:

FileSystemView.getFileSystemView().getRoots();


这些都是编写上述的refresh()方法所需的所有变量。

关于移动(带有箭头(<-,->)的按钮)操作,请从treeSelectionListener内部使此变量变为全局变量:

DefaultMutableTreeNode node = (DefaultMutableTreeNode)tse.getPath().getLastPathComponent();


使用此变量(我们将其称为selectedNode),可以使用DefaultTreeModel方法:


removeNodeFromParent(MutableTreeNode node)
insertNodeInto(MutableTreeNode newChild,MutableTreeNode parent,int index)


请注意,这两种方法都会触发JTree重绘。这应该足以实现您描述的操作。另外,您可以这样重写moveButton actionListener方法:

moveRightButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e){
        if (selectedNode != null) {
             //all the move-code-refresh-tre-view-here
             selectedNode = null;
        }
    }
});


selectedNode将仅由treeSelectionListener设置,因此以这种方式,您可以确保仅在树视图中实际选择了文件的情况下才执行文件移动操作。

关于java - 在Java中刷新fileSystemViewer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12877096/

相关文章:

javascript - 如何使用ajax传递单个参数并获取多个值?

java - 在 Java Comparator 中比较两个 TreeNode(或 DefaultMutableTreeNode)对象

android - 在 Android 4.2.2 上安装 libhoudini(ARM 仿真)。模拟器

swing - JTree 动态节点插入

java - Swing JTree - 如何使用 Netbeans GUI 进行树单选?

java - APIGatewayV2HTTPEvent、APIGatewayProxyRequestEvent 和 APIGatewayV2ProxyRequestEvent 之间的区别

java - 无法从 Servlet 到 JSP 获取错误消息(验证)

Java 通过套接字进行多文件传输

c - 打印分区表——C程序

c - 在文件系统级别创建文件