17

まず、DefaultTreeModelを使用しないとしましょう。私は自分のTreeModelを実装しているので、DefaultXXXのものを使用できません。問題は次のとおりです。モデルが定義するいくつかのaddStuff()メソッドを使用して、基になるデータ構造にノードを追加します。次に、addStuff()関数内でtreeNodesChanged()を呼び出してリスナーに通知します(treeNodesInsertedメソッドがあることはわかっていますが、同じことです。リスナーに別のメソッドで通知するだけです)。これで、リスナーの1つがメインフォームの静的クラスになり、このリスナーは、メインフォームにも含まれているJTreeにそれ自体を更新するように指示できます。モデルからノードの一部またはすべてを「リロード」するようにJTreeに指示するにはどうすればよいですか?

更新:この質問を見つけました。まったく同じではありませんが、私が望む答えが得られます。

更新2:私の問題は、ビューア(JTree)に通知する方法ではなく、モデルからの通知後にjtreeをどのように再ロードする必要があるかということでした。

まず最初に、基になる変更を反映するためにツリーを更新することを知っている唯一の方法は、updateUI()を呼び出すか、setModel()メソッドを再利用することです。基本的に、私の問題はこれです:

モデルに変更が発生したことがTreeModelListenerに(TreeModelListener APIを介して)通知されたと仮定します。さて、今何?

JTreeがTreeModelListenerを実装していないため、この問題が発生します。したがって、私の状況では、リスナーはJTreeのコンテナー、またはリスナーを実装する内部クラスであり、Jtreeと同じコンテナーの下にあります。

それで、私がTreeModelListenerの実装であり、兄のJTreeと一緒にJFormで幸せに暮らしているとしましょう。突然、私のメソッドtreeNodesInserted(TreeModelEvent evt)が呼び出されました。私は今何をしますか?内部からJtree.updateUI()を呼び出すと、モデルのリスナーリストがConcurrentModification例外をスローします。updateUI以外のものを呼び出すことはできますか?

いろいろ試してみましたが、JTreeを更新したのはupdateUIだけでした。だから私はリスナーの外でそれをしました。JFormから、undrlying構造を変更するモデルのメソッドを呼び出すだけで、次にupdateUIを呼び出します。TreeModelListenerは使用されません。

UPDATE3:暗黙のTreeModelListenersが登録されていることがわかりました。私のモデルのaddTreeModelListener(TreeModelListener listener)実装では、デバッグsystem.out行を配置します。

System.out.println("listener added: " + listener.getClass().getCanonicalName());

jTree.setModel(model)を実行したときに、このデバッグ出力が表示されました。

追加されたリスナー:javax.swing.JTree.TreeModelHandler

追加されたリスナー:javax.swing.plaf.basic.BasicTreeUI.Handler

ConcurrentModificationExceptionは、jtree.updateUI()の呼び出しがリスナー(両方ではなく、plafのみ)を再登録するために発生するため、リスナー通知ループ内でupdateUIを呼び出すとスローされます。ツリーを更新する唯一の方法は、TreeModelListenerの外部で行うことです。より良い解決策についてのコメントやアイデアはありますか?私は何かが足りないのですか?

4

8 に答える 8

21

私は同じ「問題」に直面しました。呼び出しによって内容が更新されtreeNodesInserted()ませんでした。JTree

しかし、問題は別の場所にありました: に間違ったコンストラクターを使用しましたTreeModelEvent。私はそのようなものを作成できると思いTreeModelEventましtreeNodesInserted()た:

//-- Wrong!!
TreePath path_to_inserted_item = /*....*/ ;
TreeModelEvent tme = new TreeModelEvent(my_source, path_to_inserted_item);

これはうまくいきません。

TreeModelEventdocsに記載されているように、このコンストラクターはtreeStructureChanged(). しかし、 についてtreeNodesInserted()treeNodesRemoved()treeNodesChanged()別のコンストラクターを使用する必要があります。

TreePath path_to_parent_of_inserted_items = /*....*/ ;
int[] indices_of_inserted_items = /*....*/ ;
Object[] inserted_items = /*....*/ ;
TreeModelEvent tme = new TreeModelEvent(
      my_source,
      path_to_parent_of_inserted_items,
      indices_of_inserted_items,
      inserted_items
   );

このコードは機能し、JTreeその内容を適切に更新します。


UPD:TreeModelEvent実際には、ドキュメントはこれらの s の使用、特に の使用について不明確ですJTree。そのため、これらすべてを処理する方法を理解しようとしたときに発生したいくつかの質問についてお話ししたいと思います。

まず、Paralife がコメントしたように、ノードが挿入/変更/削除された場合、またはツリー構造が変更された場合は直交しません。そう、

質問 #1:treeNodesInserted()いつ/ Changed()/を使用する必要がRemoved()ありtreeStructureChanged()ますか?

回答: // treeNodesInserted()影響を受けるすべてのノードが同じ親を持つ場合にのみ使用できますChanged()Removed()それ以外の場合は、これらのメソッドを複数回呼び出すか、treeStructureChanged()1 回だけ呼び出す (影響を受けるノードのルート ノードを渡す) ことができます。したがって、treeStructureChanged()は一種の普遍的な方法ですが、treeNodesInserted()/ Changed()/Removed()はより具体的です。

質問 #2:普遍的な方法である限りtreeStructureChanged()、なぜこれらのtreeNodesInserted()/ Changed()/に対処する必要があるのRemoved()ですか? に電話するだけのtreeStructureChanged()方が簡単なようです。

回答:ツリーの内容を表示するためにを使用している場合JTree、次のことに驚くかもしれません (私もそうでした) : を呼び出すとtreeStructureChanged()JTreeサブノードの展開状態が保持されません! JTree例を考えてみましょう。今の内容は次のとおりです。

[A]
 |-[B]
 |-[C]
 |  |-[E]
 |  |  |-[G]
 |  |  |-[H]
 |  |-[F]
 |     |-[I]
 |     |-[J]
 |     |-[K]
 |-[D]

次に、にいくつかの変更を加えC(たとえば、名前を に変更しC2ます)、それを呼び出しますtreeStructureChanged()

  myTreeModel.treeStructureChanged(
        new TreeModelEvent(
           this,
           new Object[] { myNodeA, myNodeC } // Path to changed node
           )
        );

すると、ノードEFが折りたたまれます!そして、あなたのJTree意志は次のようになります:

[A]
 |-[B]
 |-[C2]
 |  +-[E]
 |  +-[F]
 |-[D]

それを避けるには、次treeNodesChanged()のように使用する必要があります。

  myTreeModel.treeNodesChanged(
        new TreeModelEvent(
           this,
           new Object[] { myNodeA }, // Path to the _parent_ of changed item
           new int[] { 1 },          // Indexes of changed nodes
           new Object[] { myNodeC }, // Objects represents changed nodes
                                     //    (Note: old ones!!! 
                                     //     I.e. not "C2", but "C",
                                     //     in this example)
           )
        );

その後、展開状態が保持されます。


この投稿が誰かの役に立てば幸いです。

于 2013-01-18T21:16:43.407 に答える
17

私はいつも TreeModel が少し混乱していることに気づきました。ビューが自分自身を再描画できるように、変更が加えられたときにモデルがビューに通知する必要があるという上記のステートメントに同意します。ただし、これは DefaultTreeModel を使用する場合には当てはまらないようです。モデルを更新した後、reload() メソッドを呼び出す必要があることがわかりました。何かのようなもの:

DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot();
root.add(new DefaultMutableTreeNode("another_child"));
model.reload(root);
于 2009-12-16T16:57:50.900 に答える
6

昨日、同じ問題を修正するのに苦労しました。要件は、展開されたツリー ノードを折りたたむことなく、その場でノードを挿入および削除することでした。このスレッドに出くわすまで、私はウェブを閲覧し、可能な解決策をたくさん見つけました。次に、「Dmitry Frank」の anwser をTreeModelEvent. 単純なノードを挿入または削除し、残りの部分をそのままにしておくことがなぜそんなに大きな問題なのか、少し混乱しましたJTree! 最後に、 java2sからの単純なバニラの例は、おそらく最も単純な解決策を見つけるのに役立ちました。(次のような呼び出し: nodeStructureChangednodeChanged、なども、'Dmitry Frank' によって提案されたnodesWereRemovedlikeも必要ありませんでした。)nodesWereInsertedTreeModelEvent


これが私の解決策です:

// Remove a node
treeModel.removeNodeFromParent(myNode);

// Insert a node (Actually this is a reinsert, since I used a Map called `droppedNodes` to keep the previously removed nodes. So I don't need to reload the data items of those nodes.)
MyNode root = treeModel.getRootNode();
treeModel.insertNodeInto(droppedNodes.get(nodeName), root, root.getChildCount());

複雑にしないでおく ;)

于 2014-02-15T11:11:17.913 に答える
5

また、ツリーがフォルダー (ルート) とファイル (子) だけで構成されている場合、TreeModel の実装が少し混乱することもわかったので、DefaultTreeModel を使用しました。これは私にとってはうまくいきます。このメソッドは、JTree を作成または更新します。お役に立てれば。

    public void constructTree() {       

        DefaultMutableTreeNode root =
                new DefaultMutableTreeNode(UbaEnv.DB_CONFIG);
        DefaultMutableTreeNode child = null;

        HashMap<String, DbConfig> dbConfigs = env.getDbConfigs();
        Iterator it = dbConfigs.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            child = new DefaultMutableTreeNode(pair.getKey());

            child.add(new DefaultMutableTreeNode(UbaEnv.PROP));
            child.add(new DefaultMutableTreeNode(UbaEnv.SQL));
            root.add(child);
        }

        if (tree == null) {
            tree = new JTree(new DefaultTreeModel(root));
            tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
            tree.addTreeSelectionListener(new TreeListener());
        } else {
            tree.setModel(new DefaultTreeModel(root));
        }
}
于 2010-02-03T03:09:54.633 に答える
3

TreeModel は変更時に TreeModelEvents を起動し、JTree は TreeModelListener を介してモデルを監視し、モデルが変更されたときにそれ自体を更新します。

したがって、TreeModelListener サポートを正しく実装すれば、モデルを監視して JTree に通知する必要はありません。JTree 自体が既に行っているためです。MVC の観点から見ると、JTree はビュー/コントローラーであり、TreeModel はモデル (またはモデル アダプター) であり、監視可能です。

repaint() を呼び出して、JTree の表示状態を強制的に更新することもできますが、動作が保証されていないため、そうしないことをお勧めします。TreeModelListener への詳細な通知を行う方法がわからない場合は、TreeModelListener.treeStructureChanged(..) を使用して「モデル全体」の更新を通知します (警告: 選択とノードの展開状態が失われる可能性があります)。

于 2009-12-16T11:06:13.357 に答える
2

最終更新: 問題を見つけて解決しました:次の手順で問題を解決します(ただし、より良い解決策と問題の詳細な説明については、受け入れられた回答を参照してください)

  1. 登録された暗黙のリスナーは、仕事をするのに十分です。独自のリスナーを実装する必要はありません。
  2. ノードを追加して呼び出しtreeNodesInserted()ても機能しません (JTree が更新されません)。しかし、それは呼び出しで動作しtreeStructureChanged()ます。

どうやら暗黙のリスナーは内部的にツリーをリフレッシュしていますが、treeStructureChanged()メソッドの実装のみです。手動で呼び出せるようにするために、JTree がこの「アルゴリズム」を関数として提供するとよいでしょう。

于 2009-12-17T10:34:03.360 に答える
0

例: Jtree を動的に更新する

package package_name;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

public final class homepage extends javax.swing.JFrame implements ActionListener
{
     DefaultTreeModel model;
     DefaultMutableTreeNode root;
     File currentDir = new File("database");

public homepage() throws InterruptedException {
        initComponents();
    //------full screen window
        this.setExtendedState(MAXIMIZED_BOTH);
    //====================Jtree Added Details..   

    //result is the variable name for jtree
        model =(DefaultTreeModel) treedbnm.getModel();
    //gets the root of the current model used only once at the starting
        root=(DefaultMutableTreeNode) model.getRoot();
    //function called   
        dbcreate_panel1();
        Display_DirectoryInfo(currentDir,root);

}//End homepage() Constructor
public void Display_DirectoryInfo(File dir,DefaultMutableTreeNode tmp_root) throws InterruptedException 
{..........   
}
public void dbcreate_panel1()
{
    //------------------Jtree Refresh Dynamically-------------------//
             root.removeFromParent();
             root.removeAllChildren();
             model.reload();
             Display_DirectoryInfo(currentDir,root);
}
}//End homepage
于 2016-08-20T05:51:05.533 に答える