如何在编辑节点后不折叠JTree中的节点

时间:2012-05-03 16:03:59

标签: java swing jtree

我想知道如何实现此功能:

我有可编辑的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;
}

1 个答案:

答案 0 :(得分:0)

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

尝试从valueForPathChanged

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

顺便说一下,实际触发fireTreeStructureChanged的{​​{1}}的名称非常具有误导性。