2013-02-22 5 views
5

私は、サーバー上のファイルシステムのセクションを反映するファイルの階層を表示するために使用されるDefaultTreeModelでバックアップされたJTreeを含むアプリケーションを持っています。これは私のクライアントアプリ)です。私はクライアントアプリケーションが表示する必要があるデータを提供するサーバーアプリケーションも持っています(私はこれをサーバーアプリケーションと呼んでいます)。私は、ユーザーがそれらに興味を持っている場合、ファイルをツリーにロードするだけで済むように、「lazily load children」アプローチを使用しています。遅延ロードアプローチ:私は拡張ノードとなるように選択パスを設定treeWillExpand(TreeExpansionEvent evt) 遅延ロードによるJTreeの展開/選択状態の復元

  • オーバーライド

    1. 次に、そのノードの子を尋ねるメッセージをサーバーに送信します。
    2. サーバが応答すると、最後に選択されたパスコンポーネントが取得されます。
    3. 次に、返されたデータファイルごとにDefaultTreeModel.insertNodeInto()を使用します。
    4. 最後に私はDefaultTreeModel.nodeStructureChanged()と呼んでいます。

    上記はうまく動作し、子供の遅延読み込みに問題はありません。私の問題は、新しいデータがサーバーにアップロードされたときに新しいデータを含めるだけでなく、ツリーを更新する前の状態に展開状態と選択したノードを設定するようにツリーを更新する場合です(ユーザー新しいデータが表示されているからといって、ツリーの周りにはじゃまにならない)。

    1. 新しいデータがサーバーにアップロードされ
    2. サーバーアプリケーションのアーカイブ、このデータをアップロードし、ファイルに関する情報をデータベースに取り込みます。次のように流れがあります。
    3. サーバーアプリケーションは、クライアントアプリケーションに新しいデータがアップロードされたことを通知します。
    4. クライアントアプリはJTree.getExpandedDescendants()
    5. クライアントアプリケーションを使用して、ツリーの展開状態を保存JTree.getSelectionPath()
    6. クライアントアプリはDefaultTreeModelのからのすべてのノードを削除使用してツリーの選択パスを保存します。
    7. クライアントアプリケーションは、サーバーからルートノードで始まるデータを要求します。
    8. クライアントアプリケーションは、JTree.getExpandedDescendants()から返されたツリーパス列挙をトラバースし、列挙内の各TreePathにJTree.expandPath()を呼び出します。
    9. クライアントアプリケーションは、選択したツリーパスを設定します。

    私の問題は、ツリーのGUIを試しても、拡張状態を反映するように更新されないということです。 expandPathへの呼び出しごとに、クライアントから送信されたデータの要求と、サーバーからのデータによる応答を見ることができるので、expandPathへの呼び出しが機能していることはわかっています。また、現在選択されているノードに関する情報を別のウィンドウに表示し、正しく選択されたノードを表示しています。しかし、残念ながら、私の失望のために、GUIはルートノード(展開)とその子(展開されていない)のみを展開した状態の代わりに表示します。

    私の質問は次のとおりです。データモデルの更新前後でGUIが同じままになるように、JTreeの拡張状態を復元するにはどうすればよいですか?

    これらは私がしようとしているもののほんの一部です:

    • 私は私のために同様の設定でa threadを発見し、彼の問題はequals()hashCode()をオーバーライドすることで解決したが、それは私のために動作しませんでした。 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.S.これは私の最初の質問ですので、私が正しく尋ねている場合は教えてください。

  • +0

    うわー、役に立つ情報がたくさんあります。古いノードに基づいて新しいノードを使ってツリーパスを再構築する必要があると私は思っています。私はあなたが各ノードを識別するための何らかの方法を持っていると思いますか?すべての展開されたパスを表す単純な 'String'(例えば)を生成します。これらの '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); 
    } 
    
    関連する問題