问题
I have a JTree
and a JTextField
. When I select a node on the tree, the text field will display the value of the node. I can then edit the text and save it to change the selected node's value. I use the DefaultTreeModel
's nodeChanged
method to update the tree.
Is this a proper way to tell the tree model to update its node? To me it looks ugly because I'm explicitly accessing the tree's model and telling it that something has happened.
Here's some code
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class TextPaneTest extends JFrame {
private JTextField textBox = null;
private JTree tree = null;
private JButton button = null;
public static void main(String args[]) {
new TextPaneTest();
}
public TextPaneTest() {
// Main panel
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
// Panel holding tree
JPanel treePanel = new JPanel();
treePanel.setLayout(new BorderLayout());
// Panel holding text field and button
JPanel editPanel = new JPanel();
editPanel.setLayout(new BorderLayout());
// My textbox
textBox = new JTextField();
// button
button = new JButton();
button.setText("Save changes");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
String text = textBox.getText();
node.setUserObject(text);
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
model.nodeChanged(node);
}
});
// My Tree
DefaultMutableTreeNode top = new DefaultMutableTreeNode("Root");
tree = new JTree(top);
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
textBox.setText(node.getUserObject().toString());
}
});
JScrollPane scrollPane = new JScrollPane(tree);
scrollPane.setHorizontalScrollBarPolicy(
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Add tree
treePanel.add(scrollPane);
panel.add(treePanel, BorderLayout.CENTER);
// Add box and button to edit panel
editPanel.add(textBox, BorderLayout.CENTER);
editPanel.add(button, BorderLayout.SOUTH);
// Add edit panel
panel.add(editPanel, BorderLayout.SOUTH);
// Add everything to the frame
this.add(panel);
this.setSize(300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
回答1:
From poking around the JavaDoc and code for DefaultTreeModel and DefaultMutableTreeNode, I think what you are doing is ok. If you had changed the structure of the model (say by removing a node) then you would not need to call anything on the model, since the TreeModel would know that you did something to it. However, in this case, you are changing the contents of a node that the model references. So it seems reasonable that you would have to inform the TreeModel that the contents of one of its nodes is now different.
I may be missing something myself, but I think what you have is fine.
回答2:
Just for completeness: it might look slightly less ugly if you updated the node exclusively via model api
TreePath selected = tree.getSelectionPath();
tree.getModel().valueForPathChanged(selected, textBox.getText());
回答3:
Your example appears correct. Expanding on @matt's correct answer, this MVC illustration may offer some insight. Because the JTree
view also allows editing, it may invoke methods of the model. The model in turn notifies all listeners, including the tree itself, by invoking fireTreeNodesChanged()
on you behalf. To see the effect, add another listener:
tree.getModel().addTreeModelListener(new TreeModelListener() {
@Override
public void treeNodesChanged(TreeModelEvent e) {
update(e);
}
@Override
public void treeNodesInserted(TreeModelEvent e) {
update(e);
}
@Override
public void treeNodesRemoved(TreeModelEvent e) {
update(e);
}
@Override
public void treeStructureChanged(TreeModelEvent e) {
update(e);
}
private void update(TreeModelEvent e) {
System.out.println(e);
}
});
Additional notes:
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
Instead of using
setSize()
, override the tree panel's getPreferredSize() method andpack()
the enclosing frame.JPanel treePanel = new JPanel(){ @Override public Dimension getPreferredSize() { return new Dimension(300, 200); } }; … this.pack();
来源:https://stackoverflow.com/questions/19432197/is-it-proper-to-update-a-tree-node-using-nodechanged