2012-05-03 58 views
0

后,我想知道如何实现此功能:如何不折叠节点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个字符,而第5个字符也是空白。所以在我完成编辑标签后,原始标签的大小没有改变。同样,当我扩展组的名称时,例如 (例如,我将其重命名为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

为什么它首先崩溃?分享[SSCCE](http://sscce.org/)来说明问题。 – tenorsax

+0

@Max我添加了一些代码的解释,希望它会有所帮助... – stewenson

回答

0

未正确重新绘制可能与你火treeNodesChanged事件仅root在事实与标签的更新问题标识路径的数组。

尝试调用从valueForPathChanged如下:

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

顺便说一句,你的名字fireTreeStructureChanged实际上触发treeNodesChanged是非常误导的。

+0

好吧,因为我实现了TreeModel并且不扩展DefaultTreeModel,所以尝试它并没有意义,因为为了使用它,我必须对它进行编码...我想实际上是将它从TreeModel接口的实现改写为提供此metod的DefaultTreeModel的扩展。 – stewenson

+0

@ Stefan.M看看我的重新回答可以帮助你。 – tenorsax

+0

您使用错误的'TreeModelEvent'构造函数。正如[文档中所述](http://goo.gl/QL3br)所述,此构造函数只能用于** treeStructureChanged()。有关详细信息,请参见[本答案](http://goo.gl/dZDT5)。 –