2012-10-13 54 views
0

Im嘗試開發一個使用gui從系統上任何位置移動文件的小型應用程序。我有代碼來移動文件,他們確實移動時選擇和按鈕按下,但我不知道如何刷新文件系統查看器來反映更改。我必須建立系統瀏覽器的代碼如下:刷新java中的fileSystemViewer

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

請爲FileTreeCellRenderer的郵政編碼 – linski

+0

我添加了請求的代碼 –

+0

要更快,當我按下刷新按鈕時,你想樹刷新到當前的FS內容? (因爲我目前看不到)? – linski

回答

1

因爲從你的代碼看起來你知道自己在做什麼,我就展示的基本例子將只有在第一次按刷新按鈕工作:

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。

你需要調用removeNodeFromParenttreeModel

通知它從其父節點中移除節點。這將消息nodesWereRemoved創建適當的事件。這是在爲您處理事件創建時刪除節點的首選方法。

也看到這個example也。

如果你想刷新整個視圖,你應該在初始化時重新創建引用函數中的模型,或者迭代模型並根據需要進行更新 - 但是我建議在這種情況下模型的breadth first traversal

編輯:

「所以以後的一舉一動刷新將這些文件,他們現在應該再次顯示FS」。讓我們從重新初始化模型開始,我只是指DefaultTreeModel類的treeModel實例。

因此,在刷新()方法創建一個新的TreeModel實例,用DefaultMutableTreeNode實例填充它就像你在createSystemView做()方法​​:

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

APIdoc用於重載()方法here

如果您想要更新刷新函數中的模型,我相信這對於名爲「刷新」的操作更加自然,您需要獲取新的第一級目錄,然後遍歷樹這個(因爲我們遍歷所有根的孩子,這將是所提到的廣度優先遍歷):

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。解決的辦法是讓另一個

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

並在//更新邏輯部分1處(insde循環),而不是從模型中移除節點,你把它在該列表中。迭代循環時,只需遍歷該列表,並在//更新邏輯第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的使用:

請注意,這兩種方法都會觸發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進行設置,因此以這種方式你肯定,如果一個文件實際上是在文件移動操作纔會發生在樹形視圖中選擇。

+0

當你說在刷新函數中重新創建模型時,我已經嘗試過類似的東西,但它似乎不起作用,我移動的文件仍然出現:private void refresh(){JScrollPane newScroll = new JScrollPane(createSystemView()); this.areaLeft.removeAll(); this.areaLeft.add(newScroll); this.openFrame.repaint(); } –

+0

您試圖重新初始化teh * UI *而不是* model *。明天再見 – linski

+0

隨着您刷新模型的第一個解決方案,我在tree.setModel()方法中得到一個空指針異常。而對於你的第二個解決方案,我不太確定我理解要在更新邏輯1和2中放置什麼,你是說刪除每一個節點還是僅僅移動了已經移動的節點,並且在移動時添加該節點 –