编辑节点后如何不折叠JTree中的节点 [英] How to not collapse the node in JTree after the node is edited

查看:104
本文介绍了编辑节点后如何不折叠JTree中的节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to know how to implement this feature:

我有可编辑的JTree,可以在其中编辑节点的名称.如果我有一些节点是分支节点(其中有一些叶节点),并且该分支节点在编辑时被展开,那么在编辑后,该节点将被折叠.

I have editable JTree where I can edit name of the nodes. If I have some node which is branch node (it has some leaf nodes in it) and this branch node is expanded while editing, after editing, this node will be collapsed.

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

I want to leave that branch node open if it is open and collapsed if it is collapsed, after editing is done.

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

I tried to look at TreeWillExpandListener but it seems it does not solve my problem because I need to recognize if actual node is in edit mode BEFORE I call these methods ...

该技巧如何实现?显而易见,这是必要的事情,但我根本找不到答案:/

How to do this trick? It is so obvious it is necessary thing but I can't find the answer at all :/

好的,这是代码,我将尽力解释.首先,我有一个实现TreeModel的ContactTreeModel类.构造函数仅从主应用程序框架加载地址簿和组管理器,然后创建第二个根目录,并从数据库中以第二种方法加载数据.

Ok so this is the code, I'll try to explain it. First of all, I have the class ContactTreeModel which implements TreeModel. The constructor just loads addressbook and group manager from the main app frame, I create new root and I load data from the database in the second method.

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

我了解到方法

I read that the method valueForPathChanged in the TreeModel is fired up if

在用户更改了由newValue路径标识的项目的值时通知.如果newValue表示一个真正的新值,则该模型应发布一个treeNodesChanged事件.

Messaged when the user has altered the value for the item identified by path to newValue. If newValue signifies a truly new value the model should post a treeNodesChanged event.

所以我这样写了这样的方法:

So I wrote that method like this:

@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块中的方法,该方法总是被触发.看起来像

Notice the method in the finally block, that method is always fired up. It looks like

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

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

This is little bit tricky and the reason why it does not work (I guess). I just iterate through all treeModelListeners and fire up the treeNodesChanged method.

我也为Tree模型侦听器指定了数组,并将其作为ContactTreeModel和相关方法中的私有属性:

I have the array specified for tree model listeners as a private attribute in the ContactTreeModel and related methods too:

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

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

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

最后一件事,我添加到模型中的模型侦听器的外观如何?来了:

The last thing, how does the model listener I added to the model look like? Here it comes:

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

所以它基本上什么也不做.我在另一个地方注册了侦听器,现在没关系.

So it does basically nothing. I registered listener in the other place, it does not matter right now.

因此,当我执行它时,在这种状态下,树不会折叠,并且行为是所需的.但是,即使它确实重写了该节点(标签)的名称,如果原始字符串大约有5个字符,而我将其更改为例如4个字符,则标签中将只有4个字符,而第5个将有空白.因此,完成标签编辑后,原始标签的大小没有更改.同样,当我扩展组的名称时, 例如,我将其重命名为4至5个字符,该节点的标签将包含指示 整个文本太大而无法显示.太奇怪了...我怎么做udpdate 标签的?

So, when I execute it, in this state, the tree does NOT collapse and the behavior is as wanted. But even it really rewrite the name of that node (label), if the original string was about 5 characters and I changed it e.g to 4 characters, there will be only 4 characters in the label but also the empty space for the fifth. So the size of the original label has not changed after I finished editing that label. Similarly, when I expand the name of the group, e.g I rename it from 4 to 5 characters, the label of that node will contain dots indicating that the whole text is too large to display. That's weird ... How do I make the udpdate of that label?

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

The very last thing ... since I have custom icons in the JTree + I do the recognition between empty and non-empty group, I need to check the actual icon in db every time I do some action in the tree (I checked it is executed even I just open and close the nodes). This is very inefficient so I extended DefaultTreeCellRenderer where I do the actual caching:

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

推荐答案

未正确重新绘制标签的更新问题可能与以下事实有关:您触发了仅在数组中使用roottreeNodesChanged事件标识路径.

The problem with update of the label that is not repainted properly is probably related to the fact that you fire treeNodesChanged event with only root in the array that identifies the path.

尝试从valueForPathChanged调用以下内容:

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

顺便说一句,您实际上触发了treeNodesChangedfireTreeStructureChanged的名称非常容易引起误解.

By the way, your name for fireTreeStructureChanged that actually fires treeNodesChanged is very misleading.

这篇关于编辑节点后如何不折叠JTree中的节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆