2013-02-22 6 views
5

내 서버에서 파일 시스템의 섹션을 반영하는 파일 계층 구조를 표시하는 데 사용되는 DefaultTreeModel로 백업 된 JTree를 포함하는 응용 프로그램이 있습니다. 이 내 클라이언트 응용 프로그램). 또한 클라이언트 응용 프로그램에서 표시해야하는 데이터를 제공하는 서버 응용 프로그램이 있습니다.이 응용 프로그램은 서버 응용 프로그램으로 표시됩니다. 나는 "lazily load children"접근법을 사용하여 사용자가 관심이 있다면 파일을 내 트리에로드하기 만하면된다. 게으른로드 방법 : 내가 확대 노드의을로 선택 경로를 설정 treeWillExpand(TreeExpansionEvent evt) 지연로드가있는 JTree에서 확장/선택 상태를 복원합니다.

  • 오버라이드 (override)

    1. .
    2. 그런 다음 해당 노드의 하위 노드를 요청하는 메시지를 서버로 보냅니다.
    3. 서버가 응답하면 마지막으로 선택한 경로 구성 요소가 나타납니다.
    4. 그런 다음 반환 된 각 데이터 파일에 대해 DefaultTreeModel.insertNodeInto()을 사용합니다.
    5. 마지막으로 DefaultTreeModel.nodeStructureChanged()으로 전화합니다.

    위 작업은 정상적으로 작동하며 아이들을 느리게로드하는 데 문제가 없습니다. 새 데이터가 서버에 업로드 될 때 내 문제가 발생하고 새 데이터를 포함 할뿐만 아니라 트리를 업데이트하기 전에 확장 상태 및 선택한 노드를 설정하기 위해 트리를 업데이트하려고합니다 (따라서 사용자가 볼 수있는 새로운 데이터가 있기 때문에 나무 주위에 삐걱 거리지 않습니다.)

    1. 새로운 데이터가 서버에 업로드됩니다
    2. Server 응용 프로그램 아카이브 데이터를하고 업로드 된 파일에 대한 정보와 데이터베이스를 채 웁니다 다음과 같이 흐름이다.
    3. 서버 응용 프로그램은 클라이언트 응용 프로그램에 새 데이터가 업로드되었음을 알립니다.
    4. 클라이언트 응용 프로그램은 JTree.getExpandedDescendants()
    5. 클라이언트 응용 프로그램을 사용하여 트리의 확장 상태가 JTree.getSelectionPath()
    6. 클라이언트 응용 프로그램을 사용하여 나무의 선택 경로가 DefaultTreeModel의 모든 노드를 제거 저장 저장합니다.
    7. 클라이언트 응용 프로그램은 루트 노드로 시작하는 서버에서 데이터를 요청합니다.
    8. 클라이언트 응용 프로그램은 JTree.getExpandedDescendants()에서 반환 된 트리 경로 열거를 트래버스하여 열거 형의 각 TreePath에서 JTree.expandPath()을 호출합니다.
    9. 클라이언트 앱이 선택한 트리 경로를 설정합니다.

    내 문제는 트리의 GUI를 확장해도 확장 상태를 반영하지 않는다는 것입니다. 나는 expandPath에 대한 각 호출마다 클라이언트에서 보낸 데이터에 대한 요청과 서버의 데이터로 응답을 볼 수 있기 때문에 expandPath에 대한 내 호출이 작동하고 있음을 알고 있습니다. 또한 현재 선택된 노드에 대한 정보를 다른 창에 표시하고 올바르게 선택된 노드를 표시하고 있습니다. 그러나 실망스럽게도 GUI는 이전 확장 상태 대신 루트 노드 (확장 됨)와 자식 (확장되지 않음) 만 표시합니다.

    내 질문은 : 데이터 모델 업데이트 전후에 GUI가 동일하게 유지되도록 JTree의 확장 상태를 복원하려면 어떻게해야합니까?

    내가 시도하는 것들 중 몇 가지 있습니다 :

    • 내가 equals()hashCode()를 재정 의하여 해결되었다 내 자신의 문제에 대한 비슷한 설정과 a thread을 발견하지만 나를 위해 작동하지 않았다. setExpandsSelectedPaths(true), nodeStructureChanged()가 확장 상태를 저장에 JTree.invalidate()
    • 많은 다른 변화가하지만, 내가 올바른 데이터를 다시 전달되는 것을 볼 수있는 확장 상태가 올바르지 생각하지 않는다 :
    • 다양한 방법과 같은 확장을 호출 할 클라이언트 응용 프로그램과 서버 응용 프로그램간에 당신이 제공 할 수있는 모든 도움에 미리

      package tree.sscce; 
      import javax.swing.JFrame; 
      import javax.swing.JPanel; 
      import javax.swing.SwingUtilities; 
      import java.awt.BorderLayout; 
      import javax.swing.JScrollPane; 
      import javax.swing.JTree; 
      import javax.swing.JButton; 
      import java.util.Enumeration; 
      import javax.swing.BoxLayout; 
      import javax.swing.event.TreeExpansionEvent; 
      import javax.swing.event.TreeWillExpandListener; 
      import javax.swing.tree.DefaultMutableTreeNode; 
      import javax.swing.tree.DefaultTreeModel; 
      import javax.swing.tree.ExpandVetoException; 
      import javax.swing.tree.MutableTreeNode; 
      import javax.swing.tree.TreePath; 
      import java.awt.event.ActionListener; 
      import java.awt.event.ActionEvent; 
      import javax.swing.JTextPane; 
      
      public class TreeSSCCE extends JFrame implements TreeWillExpandListener { 
      
      private static final long serialVersionUID = -1930472429779070045L; 
      
      public static void main(String[] args) 
      { 
          SwingUtilities.invokeLater(new Runnable() { 
           public void run() { 
            TreeSSCCE inst = new TreeSSCCE(); 
            inst.setLocationRelativeTo(null); 
            inst.setVisible(true); 
            inst.setDefaultCloseOperation(EXIT_ON_CLOSE); 
           }   
          }); 
      } 
      
      private DefaultMutableTreeNode rootNode; 
      private JTree tree; 
      private DefaultTreeModel treeModel; 
      private TreePath selectionPathPriorToNewData; 
      private Enumeration<TreePath> expandedPathsPriorToNewData; 
      private int treeSize = 5; 
      
      public TreeSSCCE() { 
      
          this.setBounds(0, 0, 500, 400); 
          JPanel mainPanel = new JPanel(); 
          getContentPane().add(mainPanel, BorderLayout.CENTER); 
          mainPanel.setBounds(0, 0, 500, 400); 
          mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); 
      
          JPanel descriptionPanel = new JPanel(); 
          descriptionPanel.setBounds(0, 0, 500, 200); 
          mainPanel.add(descriptionPanel); 
      
          JTextPane textPane = new JTextPane(); 
          String newLine = System.getProperty("line.separator"); 
          descriptionPanel.setLayout(new BorderLayout(0, 0)); 
          textPane.setText("Start by expanding some nodes then click 'Add New Data' and you will notice that the tree state is not retained."); 
          descriptionPanel.add(textPane); 
      
          // Initialize The Tree 
          tree = new JTree(); 
          rootNode = new DefaultMutableTreeNode("Root"); 
          treeModel = new DefaultTreeModel(rootNode); 
          tree.addTreeWillExpandListener(this); 
          tree.setModel(treeModel); 
          tree.setShowsRootHandles(true); 
          populateTree(false); 
      
          JScrollPane scrollPane = new JScrollPane(tree); 
          mainPanel.add(scrollPane); 
      
          JPanel buttonPanel = new JPanel(); 
          mainPanel.add(buttonPanel); 
      
          JButton btnAddNewData = new JButton("Add New Data"); 
          btnAddNewData.addActionListener(new ActionListener() { 
           public void actionPerformed(ActionEvent arg0) { 
            addNewDataToTree(); 
           } 
          }); 
          buttonPanel.add(btnAddNewData); 
      
      
      } 
      
      private void removeAllTreeNodes() 
      { 
      
          while(!treeModel.isLeaf(treeModel.getRoot())) 
          { 
           treeModel.removeNodeFromParent((MutableTreeNode)treeModel.getChild(treeModel.getRoot(),0)); 
          } 
          treeModel = null; 
          treeModel = new DefaultTreeModel(rootNode); 
          tree.setModel(treeModel); 
      } 
      
      public void restoreExpansionState(Enumeration enumeration) 
      { 
          if (enumeration != null) 
          { 
           while (enumeration.hasMoreElements()) 
           { 
            TreePath treePath = (TreePath) enumeration.nextElement(); 
            tree.expandPath(treePath); 
            tree.setSelectionPath(treePath); 
           } 
           tree.setSelectionPath(selectionPathPriorToNewData); 
          } 
      } 
      
      protected void addNewDataToTree() 
      { 
          // save the tree state 
          selectionPathPriorToNewData = tree.getSelectionPath(); 
          expandedPathsPriorToNewData = tree.getExpandedDescendants(new TreePath(tree.getModel().getRoot())); 
          removeAllTreeNodes(); 
          populateTree(true); 
          restoreExpansionState(expandedPathsPriorToNewData); 
      } 
      
      private void populateTree(boolean newData) 
      { 
          if(newData) 
           treeSize++; 
          MyParentNode[] parents = new MyParentNode[treeSize]; 
          for(int i = 0; i < treeSize; i++) 
          { 
           parents[i] = new MyParentNode("Parent [" + i + "]"); 
           treeModel.insertNodeInto(parents[i], rootNode, i); 
          } 
      } 
      
      @Override 
      public void treeWillCollapse(TreeExpansionEvent evt) throws ExpandVetoException { 
          // Not used. 
      } 
      
      @Override 
      public void treeWillExpand(TreeExpansionEvent evt) throws ExpandVetoException 
      { 
          System.out.println("Tree expanding: " + evt.getPath()); 
          tree.setExpandsSelectedPaths(true); 
          tree.setSelectionPath(evt.getPath()); 
          // we have already loaded the top-level items below root when we 
          // connected so lets just return... 
          if(evt.getPath().getLastPathComponent().equals(treeModel.getRoot())) 
           return; 
      
          // if this is not root lets figure out what we need to do. 
          DefaultMutableTreeNode expandingNode = (DefaultMutableTreeNode) evt.getPath().getLastPathComponent(); 
      
          // if this node already has children then we don't want to reload so lets return; 
          if(expandingNode.getChildCount() > 0) 
           return;  
          // if this node has no children then lets add some 
          MyParentNode mpn = new MyParentNode("Parent Under " + expandingNode.toString()); 
          treeModel.insertNodeInto(mpn, expandingNode, expandingNode.getChildCount()); 
          for(int i = 0; i < 3; i++) 
          { 
           treeModel.insertNodeInto(new DefaultMutableTreeNode("Node [" + i + "]"), mpn, i); 
          } 
      } 
      
      private class MyParentNode extends DefaultMutableTreeNode 
      { 
          private static final long serialVersionUID = 433317389888990065L; 
          private String name = ""; 
      
          public MyParentNode(String _name) 
          { 
           super(_name); 
           name = _name; 
          } 
      
          @Override 
          public int hashCode() { 
           final int prime = 31; 
           int result = 1; 
           result = prime * result + getOuterType().hashCode(); 
           result = prime * result + ((name == null) ? 0 : name.hashCode()); 
           return result; 
          } 
      
          @Override 
          public boolean equals(Object obj) { 
           if (this == obj) 
            return true; 
           if (obj == null) 
            return false; 
           if (getClass() != obj.getClass()) 
            return false; 
           MyParentNode other = (MyParentNode) obj; 
           if (!getOuterType().equals(other.getOuterType())) 
            return false; 
           if (name == null) { 
            if (other.name != null) 
             return false; 
           } else if (!name.equals(other.name)) 
            return false; 
           return true; 
          } 
      
          @Override 
          public boolean isLeaf() 
          { 
           return false; 
          } 
      
          private TreeSSCCE getOuterType() { 
           return TreeSSCCE.this; 
          }  
      } 
      
      } 
      

      감사 :

    여기 내 SSCCE입니다.

    P. 이것은 제 첫 질문입니다. 그래서 제가 제대로 물어보고 있다면 (그리고 쉽게 받아들이십시오;)) 알려주십시오.

  • +0

    와우, 유용한 정보가 많이 있습니다. 이전 노드를 기반으로 새 노드를 사용하여 트리 경로를 재구성해야한다고 생각합니다. 각 노드를 식별 할 수있는 방법이 있다고 가정합니까? 확장 된 모든 경로를 나타내는 간단한 '문자열'(예 :)을 생성하십시오. 이'String' 경로를 사용하여 트리를 업데이트 할 때, 사용 가능한 노드에서 새로운 트리 경로를 빌드하십시오. – MadProgrammer

    +1

    'FileTreeModel'이 여기에 있습니다 (http://stackoverflow.com/a/14837208/230513) 문제를 제거하지 않으면 코드를 단순화 할 수 있습니다. – trashgod

    +0

    @trashgod 내 노드가 응용 프로그램 특정 정보는 물론 파일에 대한 정보가있는 사용자 정의 객체이므로 'FileTreeModel'이 작동하지 않습니다. 또한, 내 클라이언트 애플 리케이션은 서버에 상주하는 이러한 파일에 직접 액세스 할 수 없습니다. – Jeremy

    답변

    1

    사용자 지정 트리 모델 (extends DefaultTreeModel)을 사용하고이를 처리하기 위해 DBTreeEvent.STRUCTURE_CHANGED 이벤트에 반응합니다. 이것은 내가 옛날 상태를 보존하기 위해하는 일입니다. 확실하지 않으면 도움이 될 것입니다 ..

    //NOTE: node is the tree node that caused the tree event 
    TreePath nodesPath = new TreePath(node.getPath()); 
    TreePath currentSel = myTree.getLeadSelectionPath(); 
    List<TreePath> currOpen = getCurrExpandedPaths(nodesPath); 
    super.nodeStructureChanged(node); 
    reExpandPaths(currOpen); 
    myTree.setSelectionPath(currentSel); 
    
    private List<TreePath> getCurrExpandedPaths(TreePath currPath) 
    { 
        List<TreePath> paths = new ArrayList<TreePath>(); 
        Enumeration<TreePath> expandEnum = myTree.getExpandedDescendants(currPath); 
        if (expandEnum == null) 
         return null; 
    
        while (expandEnum.hasMoreElements()) 
         paths.add(expandEnum.nextElement()); 
    
        return paths; 
    } 
    
    private void reExpandPaths(List<TreePath> expPaths) 
    { 
        if(expPaths == null) 
         return; 
        for(TreePath tp : expPaths) 
         myTree.expandPath(tp); 
    }