2

我有一个JTree和一个JTextField。当我在树上选择一个节点时,文本字段将显示该节点的值。然后我可以编辑文本并保存它以更改所选节点的值。我使用DefaultTreeModel'nodeChanged方法来更新树。

这是告诉树模型更新其节点的正确方法吗?对我来说它看起来很丑,因为我明确地访问了树的模型并告诉它发生了一些事情。

这是一些代码

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);
    }
}
4

3 回答 3

5

仅出于完整性考虑:如果您仅通过模型 api 更新节点,它可能看起来不那么难看

TreePath selected = tree.getSelectionPath();
tree.getModel().valueForPathChanged(selected, textBox.getText());
于 2013-10-18T12:35:33.173 回答
2

你的例子似乎是正确的。扩展@matt 的正确答案,这个 MVC插图可能会提供一些见解。因为JTree视图也允许编辑,它可以调用模型的方法。该模型反过来通过fireTreeNodesChanged()代表您调用来通知所有侦听器,包括树本身。要查看效果,请添加另一个侦听器:

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);
    }
});

补充笔记:

  • Swing GUI 对象应该事件分派线程上构建和操作。

  • 而不是使用setSize(),覆盖树面板的getPreferredSize()方法和pack()封闭框架。

    JPanel treePanel = new JPanel(){
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(300, 200);
        }
    };
    …
    this.pack();
    
于 2013-10-17T20:40:56.287 回答
1

通过查看 JavaDoc 和 DefaultTreeModel 和 DefaultMutableTreeNode 的代码,我认为您正在做的事情还可以。如果您更改了模型的结构(例如通过删除一个节点),那么您不需要调用模型上的任何内容,因为 TreeModel 会知道您对它做了什么。但是,在这种情况下,您正在更改模型引用的节点的内容。因此,您必须通知 TreeModel 其节点之一的内容现在不同,这似乎是合理的。

我自己可能会遗漏一些东西,但我认为你所拥有的很好。

于 2013-10-17T16:49:04.350 回答