まず、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 Exceptionをスローします。 updateUI以外のものを呼び出すことはできますか?
多くのことを試しましたが、JTreeを更新したのはupdateUIだけでした。だから私はそれをリスナーの外でやった。 JFormから、モデルのメソッドを呼び出して、構造を変更し、次にupdateUIを呼び出します。 TreeModelListenerは使用されません。
UPDATE3:暗黙のTreeModelListenerが登録されていることがわかりました。モデルの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の外で行うことです。より良い解決策のためのコメントやアイデアはありますか?何か不足していますか?
同じ「問題」に直面しました。treeNodesInserted()
を呼び出しても、JTree
が内容を更新することはありませんでした。
しかし、問題は別の場所にありました:TreeModelEvent
に間違ったコンストラクターを使用しました。私はそのようにtreeNodesInserted()
のTreeModelEvent
を作成できると思いました:
//-- Wrong!!
TreePath path_to_inserted_item = /*....*/ ;
TreeModelEvent tme = new TreeModelEvent(my_source, path_to_inserted_item);
これは機能しません。
TreeModelEvent
docs で述べたように、このコンストラクタは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()
?
Answer:treeNodesInserted()
/Changed()
/Removed()
は、影響を受けるすべてのノードだけが持っている場合に使用できます同じ親。それ以外の場合は、これらのメソッドを複数回呼び出すか、treeStructureChanged()
を1回だけ呼び出す(そして、影響を受けるノードのルートノードをそれに渡す)ことができます。したがって、treeStructureChanged()
は一種の普遍的な方法ですが、treeNodesInserted()
/Changed()
/Removed()
はより具体的です。
質問#2:treeStructureChanged()
が普遍的な方法である限り、なぜこれらのtreeNodesInserted()
に対処する必要があるのですか?/Changed()
/Removed()
? treeStructureChanged()
を呼び出すだけの方が簡単なようです。
Answer: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
)
);
次に、ノードE
およびF
が折りたたまれます。 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)
)
);
その後、エキスパンド状態が保持されます。
この投稿が誰かにとって役立つことを願っています。
私はいつもTreeModelを少し混乱させることに気づきました。ビューがそれ自体を再描画できるように、変更が行われたときにモデルがビューに通知する必要があるという上記の記述に同意します。ただし、DefaultTreeModelを使用する場合はそうではありません。モデルを更新した後でreload()メソッドを呼び出す必要があることがわかりました。何かのようなもの:
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot();
root.add(new DefaultMutableTreeNode("another_child"));
model.reload(root);
また、ツリーがFolders(root)とFiles(child)だけで構成されていない場合、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));
}
}
昨日、私は同じ問題を修正するために奮闘しました。 要件は、展開されたツリーノードを折りたたむことなく、その場でノードを挿入および削除することでした。私はこのスレッドに出くわすまで、ウェブを閲覧して、可能な解決策の束を見つけました。次に、TreeModelEvent
を使用して「Dmitry Frank」からのアンサーを適用しました。少し混乱しました。単純なノードを挿入または削除して、残りのJTree
をそのままにしておくのはなぜそれほど大きな問題なのでしょうか。最後に Java2s の単純なバニラの例は、おそらく最も簡単な解決策を見つけるのに役立ちました。 (nodeStructureChanged
、nodeChanged
、nodesWereRemoved
、nodesWereInserted
などのような呼び出しも、 'Dmitry Frankが提案するTreeModelEvent
のような呼び出しも'が必要でした。)
これが私の解決策です:
// 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());
単純にする ;)
TreeModelは、変更時にTreeModelEventsを起動することになっています。JTreeはTreeModelListenerを介してモデルを監視し、モデルが変更されると自身を更新します。
したがって、TreeModelListenerサポートを正しく実装するの場合、モデルを監視してJTreeに通知する必要はありません。 MVCの観点から、JTreeはビュー/コントローラーであり、TreeModelはモデル(またはモデルアダプター)であり、監視可能です。
Repaint()を呼び出して、JTreeに視覚的な状態を強制的に更新させることもできますが、動作が保証されていないため、そうしないことをお勧めします。 TreeModelListenerにきめ細かい通知を行う方法がわからない場合は、TreeModelListener.treeStructureChanged(..)を使用して「モデル全体」の更新を通知します(警告:選択とノード展開の状態が失われる可能性があります)。
最終更新:問題を見つけて解決しました:次の手順で問題を解決します(ただし、より良い解決策と問題の詳細な説明については、承認された回答を参照してください):
treeNodesInserted()
を呼び出すと、機能しません(JTreeは更新されません)。しかし、これはtreeStructureChanged()
の呼び出しで機能します。どうやら、暗黙のリスナーは、私が望む方法でツリーを内部的に更新していますが、それらのtreeStructureChanged()
メソッド実装でのみです。 JTreeが手動で呼び出せるようにするために、この「アルゴリズム」を関数として提供するとよいでしょう。
モデルをnullに設定することで、ツリー全体を再初期化することが可能であるようです。
TreePath path = tree.getSelectionPath();
model.doChanges(); // do something with model
tree.setModel(null);
tree.setModel(model);
tree.setSelectionPath(path);
tree.expandPath(path);
ツリー更新は私のために働きます
krアヒム
次に例を示します。
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