【问题标题】:How to not collapse the node in JTree after the node is edited编辑节点后如何不折叠JTree中的节点
【发布时间】:2012-05-13 04:33:06
【问题描述】:

我想知道如何实现这个功能:

我有可编辑的 JTree,我可以在其中编辑节点的名称。如果我有一些节点是分支节点(它里面有一些叶子节点)并且这个分支节点在编辑时展开,编辑后这个节点将被折叠。

编辑完成后,如果该分支节点处于打开状态,我想保持该分支节点处于打开状态,如果它处于折叠状态则保持折叠状态。

我尝试查看TreeWillExpandListener,但它似乎不能解决我的问题,因为在调用这些方法之前我需要识别实际节点是否处于编辑模式...

如何做到这一点?很明显这是必要的事情,但我根本找不到答案:/

好的,这就是代码,我将尝试解释它。首先,我有实现 TreeModel 的类 ContactTreeModel。构造函数只是从主应用程序框架加载地址簿和组管理器,我创建新的根并在第二种方法中从数据库加载数据。

public ContactTreeModel() {
    addressBookManager = ContactManagerFrame.getAddressBookManager();
    groupManager = ContactManagerFrame.getGroupManager();
    root = new DefaultMutableTreeNode();
    processTreeHierarchy();
}

private void processTreeHierarchy() {
    DefaultMutableTreeNode group, contact;
    for (Group g : addressBookManager.getGroups()) {
        group = new DefaultMutableTreeNode(g);
        root.add(group);
        for (Contact c : addressBookManager.getContactsFromGroup(g)) {
            contact = new DefaultMutableTreeNode(c);
            group.add(contact);
        }
    }
}

我读到 TreeModel 中的 valueForPathChanged 方法在以下情况下被启动

当用户将路径标识的项目的值更改为 newValue 时发送消息。如果 newValue 表示一个真正的新值,模型应该发布一个 treeNodesChanged 事件。

所以我写了这样的方法:

@Override
public void valueForPathChanged(TreePath path, Object newValue) {
    // backup of the original group
    Group oldGroup = (Group) path.getLastPathComponent();
    try {
        Group testGroup = (Group) path.getLastPathComponent();
        testGroup.setName((String) newValue);
        // validation of the group to be updated
        groupManager.validateGroup(testGroup, true);
        oldGroup.setName((String) newValue);
        // updating of the group in db
        groupManager.updateGroup(oldGroup);
    } catch (ServiceFailureException | ValidationException ex) {
        // if database error occured or validation exception is raised, 
        // update label in gui 
        ContactManagerFrame.getStatusPanelLabel().setText(ex.getMessage());
    } finally {
        fireTreeStructureChanged();
    }
}

注意 finally 块中的方法,该方法总是被启动。好像

protected void fireTreeStructureChanged() {
    Object[] o = {root};
    TreeModelEvent e = new TreeModelEvent(this, o);
    for (TreeModelListener l : treeModelListeners) {
        l.treeNodesChanged(e);
    }
}

这有点棘手,也是它不起作用的原因(我猜)。我只是遍历所有 treeModelListeners 并启动 treeNodesChanged 方法。

我将树模型侦听器指定的数组作为 ContactTreeModel 中的私有属性以及相关方法:

private Vector<TreeModelListener> treeModelListeners = new Vector<>();

@Override
public void addTreeModelListener(TreeModelListener l) {
    treeModelListeners.addElement(l);
}

@Override
public void removeTreeModelListener(TreeModelListener l) {
    treeModelListeners.removeElement(l);
}

最后一件事,我添加到模型中的模型监听器长什么样?来了:

public class ContactTreeModelListener implements TreeModelListener {
    @Override
    public void treeNodesChanged(TreeModelEvent e) {
        System.out.println("nodes changed");
    }

    @Override
        public void treeNodesInserted(TreeModelEvent e) {
        System.out.println("nodes inserted");
    }

    @Override
    public void treeNodesRemoved(TreeModelEvent e) {
        System.out.println("nodes removed");
    }

    @Override
    public void treeStructureChanged(TreeModelEvent e) {
        System.out.println("structure changed");
    }
}

所以它基本上什么都不做。我在其他地方注册了监听器,现在没关系。

所以,当我执行它时,在这种状态下,树不会崩溃,并且行为是所希望的。但即使它真的重写了该节点(标签)的名称,如果原始字符串大约为 5 个字符,我将其更改为例如 4 个字符,标签中将只有 4 个字符,但还有第五个字符的空白空间。因此,在我完成编辑该标签后,原始标签的大小并没有改变。同样,当我展开组名时, 例如,我将其从 4 个字符重命名为 5 个字符,该节点的标签将包含表示 整个文本太大而无法显示。这很奇怪......我如何制作 udpdate 那个标签?

最后一件事......因为我在 JTree 中有自定义图标 + 我在空组和非空组之间进行识别,所以每次在树中执行某些操作时,我都需要检查 db 中的实际图标(我检查了它是否已执行,即使我只是打开和关闭节点)。这是非常低效的,所以我扩展了 DefaultTreeCellRenderer,我在其中进行实际缓存:

@Override
public Component getTreeCellRendererComponent(
        JTree tree,
        Object value,
        boolean sel,
        boolean expanded,
        boolean leaf,
        int row,
        boolean hasFocus) {

    if (iconCache == null) {
        throw new NullPointerException("iconCache in " 
             + this.getClass().getName() + " is null");
    }

    super.getTreeCellRendererComponent(
            tree, value, sel,
            expanded, leaf, row,
            hasFocus);

    if (value instanceof Group) {
        Group g = (Group) value;
        Icon groupIcon = iconCache.get(g);
        if (groupIcon == null) {
            if (groupHasContacts(g)) {
                groupIcon = groupNonEmpty;
            } else {
                groupIcon = groupEmptyIcon;
            }
            iconCache.put(g, groupIcon);
        }
        JLabel result = (JLabel) super.getTreeCellRendererComponent(tree,
                g.getName(), sel, expanded, leaf, row, hasFocus);

        result.setIcon(groupIcon);
        return result;
    }
    else if (value instanceof Contact) {
        Contact c = (Contact) value;
        Icon icon = iconCache.get(c);
        if (icon == null) {
            icon = this.contactIcon;
            iconCache.put(c, icon);
        }
        JLabel result = (JLabel) super.getTreeCellRendererComponent(
                tree, c.getName() + c.getSurname(), 
                sel, expanded, leaf, row, hasFocus);
        result.setIcon(icon);
        return result;
    }

    JLabel defaultNode = (JLabel) super.getTreeCellRendererComponent(
            tree, "?", sel, expanded, leaf, row, hasFocus);

    defaultNode.setIcon(unknownNode);
    return defaultNode;
}

【问题讨论】:

  • 为什么一开始就崩溃了?分享SSCCE,说明问题。
  • @Max 我为解释添加了一些代码,希望对您有所帮助...

标签: java swing jtree


【解决方案1】:

未正确重绘标签的更新问题可能与您在标识路径的数组中仅使用root 触发treeNodesChanged 事件有关。

尝试从valueForPathChanged调用以下:

public void fireTreeNodesChanged(TreePath path) {
    TreeModelEvent e = new TreeModelEvent(this, path.getPath());
    for (TreeModelListener l : treeModelListeners) {
        l.treeNodesChanged(e);
    }
}

顺便说一句,您的 fireTreeStructureChanged 名称实际上会触发 treeNodesChanged 非常具有误导性。

【讨论】:

  • 好吧,因为我实现了 TreeModel 并且没有扩展 DefaultTreeModel,所以尝试它是没有意义的,因为为了使用它我必须对其进行编码......我实际上是从实现重写它我猜,TreeModel 接口是 DefaultTreeModel 扩展的接口,这里提供了这个方法。
  • @Stefan.M 看看我重新审视的答案是否可以帮助你。
  • 你使用了错误的TreeModelEvent构造函数。作为stated in the docs,此构造函数只能用于treeStructureChanged()。详情请见this answer
猜你喜欢
  • 1970-01-01
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 2014-06-01
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多