2012-05-03 1 views
0

Я хочу знать, как реализовать эту функцию:Как не свернуть узел в JTree после того, как узел редактируется

У меня есть редактируемые JTree, где я могу изменить имя узлов. Если у меня есть узел, который является ветвящимся узлом (в нем есть некоторые листовые узлы), и этот узел ветвления расширяется при редактировании, после редактирования этот узел будет свернут.

Я хочу оставить этот ветвь открытой, если она открыта и свернута, если она свернута, после того, как редактирование выполнено.

Я попытался посмотреть на TreeWillExpandListener, но кажется, что это не решает мою проблему, потому что нужно признать, если фактический узел находится в режиме редактирования ДО Я называю эти методы ...

Как сделать этот трюк? Это настолько очевидно, что это необходимо, но я не могу найти ответ вообще:/

Хорошо, так что это код, я попробую объяснить его. Прежде всего, у меня есть класс ContactTreeModel, который реализует TreeModel. Конструктор просто загружает адресную книгу и менеджер групп из основного фрейма приложения, я создаю новый root и загружаю данные из базы данных во втором методе.

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

Я прочитал, что метод valueForPathChanged в TreeModel обжигают, если

обменивались сообщениями, когда пользователь изменил значение для элемента, идентифицированного пути к 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(); 
    } 
} 

Обратите внимание на метод в блоке, наконец, этот метод всегда разжег. Похоже, что

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

Почему это рушится в первую очередь? Поделиться [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 до расширения DefaultTreeModel, где этот метод предоставлен, я думаю. – stewenson

+0

@ Stefan.M Посмотрите, может ли мой повторный ответ помочь вам. – tenorsax

+0

Вы используете неправильный конструктор 'TreeModelEvent'. Как [указано в документах] (http://goo.gl/QL3br), этот конструктор может использоваться ** только ** для 'treeStructureChanged()'. Подробнее см. [Этот ответ] (http://goo.gl/dZDT5). –