【问题标题】:Get Edited TreeNode from a CellEditorListener从 CellEditorListener 获取已编辑的 TreeNode
【发布时间】:2012-06-21 21:45:26
【问题描述】:

之前我问过如何在重命名 TreeNode 时触发事件 (here)。我的问题得到了回答,但我遇到了另一个问题。我需要访问 CellEditorListener 的 editingStopped 事件中正在编辑的 TreeNode。这是我必须这样做的代码:

package com.gamecreator;

import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.tree.DefaultTreeCellEditor;

public class CustomCellEditorListener implements CellEditorListener {
    public CustomCellEditorListener() {

    }

    public void editingCanceled(ChangeEvent e) {

    }

    public void editingStopped(ChangeEvent e) {
        DefaultTreeCellEditor editor = (DefaultTreeCellEditor) e.getSource(); //This gives me the error.
        CustomTreeNode node = //What do I put here???;
        node.getResource().setName((String) node.getUserObject());

        //For debugging
        System.out.println(node.getResource().getName());
    }
}

我收到此错误:

线程“AWT-EventQueue-0”java.lang.ClassCastException 中的异常: javax.swing.tree.DefaultTreeCellEditor$1 不能转换为 javax.swing.tree.DefaultTreeCellEditor

编辑: 在另一次尝试中,我在 CustomCellEditorListener 中使用了这段代码

public void editingStopped(ChangeEvent e) {
    TreePath path = ((CustomTreeCellEditor) e.getSource()).getLastPath();  //This gives me the error.
    CustomTreeNode node = (CustomTreeNode) path.getLastPathComponent();
    node.getResource().setName((String) node.getUserObject());

    //For debugging
    System.out.println(node.getResource().getName());
}

以及 CustomTreeCellEditor 中的这段代码

public TreePath getLastPath() {
    return lastPath;
}

我得到了同样的错误(我希望我会)。我所拥有的应该可以工作,所以剩下的唯一真正的问题是,“为什么我会收到错误以及如何修复它?”但如果有人有更好的方法来完成这个,我愿意倾听。

编辑 2: 我已经做了一个小例子来说明我正在尝试完成的事情,可以在 here 找到(这是一个 Eclipse 存档)。

【问题讨论】:

  • 可怕的是 CellEditor(JTextField 或 JComboBox)仅在第 3 个可用。鼠标点击,
  • @mKorbel:+1 对比TreeCellEditor 接口与默认实现的答案。欢迎您对我的approach 进行批判性评论。

标签: java swing events rename treenode


【解决方案1】:

您似乎想在DefaultMutableTreeNode 中编辑Resource 的名称。如您所见,ChangeEvent 的来源发送到editingStopped()不是DefaultTreeCellEditor;它是编辑器的(匿名)UI 委托。

改为在您的DefaultTreeCellEditor 中覆盖getCellEditorValue(),如下所示。 DefaultTreeCellRenderer 只是通过convertValueToText() 调用toString(),它访问DefaultMutableTreeNode 的用户对象。

附录:注意isCellEditable() 确保只能编辑叶节点。

正如 @kleopatra 在 cmets 中指出的那样,之前的 TreeCellEditor 实现无效,因为它修改了正在编辑的节点。下面的修订版本创建了一个具有更新名称的新节点; copy constructor 在这种情况下会很有用。优点是userObject 仍然是Resource。另请参阅alternative approach

/**
 * @see https://stackoverflow.com/a/12651990/230513
 * @see https://stackoverflow.com/a/11639595/230513
 * @see https://stackoverflow.com/a/11113648/230513
 */
public class TreeEditDemo extends JPanel {

    private JTree tree;
    private DefaultMutableTreeNode root;
    private DefaultTreeCellEditor editor;
    private JLabel label = new JLabel(" ", JLabel.CENTER);

    public TreeEditDemo() {
        super(new BorderLayout());
        root = new DefaultMutableTreeNode("Nodes");
        root.add(new DefaultMutableTreeNode(new Resource("one")));
        root.add(new DefaultMutableTreeNode(new Resource("two")));
        root.add(new DefaultMutableTreeNode(new Resource("three")));
        final DefaultTreeModel treeModel = new DefaultTreeModel(root);
        tree = new JTree(treeModel);
        tree.setEditable(true);
        editor = new MyTreeCellEditor(tree,
            (DefaultTreeCellRenderer) tree.getCellRenderer());
        tree.setCellEditor(editor);
        tree.getInputMap().put(
            KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "startEditing");
        this.add(new JScrollPane(tree));
        this.add(label, BorderLayout.SOUTH);
        tree.addTreeSelectionListener(new TreeSelectionListener() {

            @Override
            public void valueChanged(TreeSelectionEvent e) {
                TreePath path = e.getNewLeadSelectionPath();
                if (path != null) {
                    DefaultMutableTreeNode node =
                        (DefaultMutableTreeNode) path.getLastPathComponent();
                    if (node.isLeaf()) {
                        Resource user = (Resource) node.getUserObject();
                        label.setText(user.toString());
                    } else {
                        label.setText(" ");
                    }
                }
            }
        });
        editor.addCellEditorListener(new CellEditorListener() {

            @Override
            public void editingStopped(ChangeEvent e) {
                label.setText(editor.getCellEditorValue().toString());
            }

            @Override
            public void editingCanceled(ChangeEvent e) {
            }
        });
    }

    private static class MyTreeCellEditor extends DefaultTreeCellEditor {

        public MyTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
            super(tree, renderer);
        }

        @Override
        public Object getCellEditorValue() {
            String value = (String) super.getCellEditorValue();
            return new Resource(value);
        }

        @Override
        public boolean isCellEditable(EventObject e) {
            return super.isCellEditable(e)
                && ((TreeNode) lastPath.getLastPathComponent()).isLeaf();
        }
    }

    private static class Resource {

        String name;

        public Resource(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return getName();
        }
    }

    private void display() {
        JFrame f = new JFrame("TreeEditorDemo");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TreeEditDemo().display();
            }
        });
    }
}

【讨论】:

  • 我实际上能够非常轻松地更改点击次数。我只是把它放在我的 CustomTreeCellEditor 类中(扩展 DefaultTreeCellEditor): protected boolean canEditImmediately(EventObject event) { if (event == null) return true; if (((MouseEvent) 事件).getClickCount() >= 2) 返回真;返回假; }
  • 为方便起见,可以将一个键绑定到现有的编辑Action:tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Fn, 0), "startEditing")
  • hmm ... 这不是一个真正有效的编辑器实现:它不得更改模型中的任何内容,就像上面在 getCellEditorValue 中所做的那样。
  • @kleopatra:我想我明白为什么在getTreeCellRendererComponent() 中会出现问题;但我认为getCellEditorValue() 是安全的,因为它被称为 编辑结束之后。如果没有,我将不得不重新处理这个例子。一如既往,感谢您的洞察力。
【解决方案2】:

我找到了一个实际上非常简单的解决方案。当重命名 TreeNode 时,它​​最终成为树中唯一选定的节点。因此,我能够使用:

    CustomTreeNode node = (CustomTreeNode) tree.getLastSelectedPathComponent();

【讨论】:

  • 我敢说这不是很理想吗?如果您的 JTree 选择模型与多项选择混淆,因此重命名不会取消其他选择怎么办?或者如果您以某种方式更改了 JTree,因此您可以在不选择的情况下进行编辑?我建议一种方法是继承 DefaultTreeCellEditor (DTCE),在构造函数中添加一个 CellEditorListener,覆盖 getTreeCellEditorComponent,然后将必须通过编辑保留的任何数据放入字段中。 DTCE 中一个有用的(受保护的)字段是“lastRow”...在大多数情况下,您可以从 editorStopped 内部访问它以返回您的节点。
猜你喜欢
  • 2014-08-20
  • 1970-01-01
  • 2012-05-03
  • 1970-01-01
  • 2013-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多