Подтвердить что ты не робот

Узлы обновления JTree без сворачивания

У меня есть приложение Java SE 7, которое нуждается в обновлении узлов JTree. Из учебника, данного Oracle, используя этот поток, нет никакого намека на то, как я могу обновить метку (отображаемый текст node на дереве) кода. В настоящее время я использую DefaultTreeModel как модель моих JTree и DefaultMutableTreeNode в качестве узлов указанного дерева.

Для более подробной информации о приложении, над которым я работаю, я создаю чат-центр, в котором контакты (контакты) отображаются с их статусом доступности (будь то Online, Offline и т.д.) для каждой учетной записи.

Вопрос в том, как я могу обновить отображаемый текст конкретного node без (не более) удаления его из него родительским и добавления его на указанный индекс. Как DefaultMutableTreeNode.setText("<new label>")?


ОБНОВЛЕНИЕ: 20 января 2013 г.

Пересмотрен вопрос о разъяснениях.

4b9b3361

Ответ 1

Пусть эта простая и исполняемая программа поможет вам решить вашу проблему.

public class JTreeDemo  extends JPanel
    implements Runnable {

private JTree tree;
private DefaultTreeModel treeModel ;
private Random rnd = new Random();
private List<User> userList;
public JTreeDemo() {
    super( );

    //Create the nodes.
    DefaultMutableTreeNode top =
        new DefaultMutableTreeNode("Users");
    treeModel = new DefaultTreeModel(top);
    createNodes(top);

    //Create a tree that allows one selection at a time.
    tree = new JTree(treeModel);
    tree.getSelectionModel().setSelectionMode
            (TreeSelectionModel.SINGLE_TREE_SELECTION);

    //Create the scroll pane and add the tree to it. 
    JScrollPane treeView = new JScrollPane(tree);


    //Add the split pane to this panel.
    add(treeView);
}

public String getRandomStatus() {
    int nextInt = rnd.nextInt(100);
    if( nextInt%2==0) {
        return "Online";
    } else {
        return "Offline";
    }
}
@Override
public void run() {
     while(true) {
        try {   
          Thread.sleep(1000);
          int nextInt = rnd.nextInt(10);
          User user = userList.get(nextInt);
          user.setStatus(getRandomStatus());
          treeModel.nodeChanged(user);
        } catch (InterruptedException ex) {
            // handle it if necessary
        }
     }
}

private class User extends DefaultMutableTreeNode {
    public String userName;
    public String status;

    public User(String name) {
        userName = name;

    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getStatus() {
        return status;
    }

    @Override
    public String toString() {
        String color = status.equals("Online") ? "Green" : "Red";
        return "<html><b color='"+color+"'>"+
                userName +"-"+status +"</b></html>";
    }

}


private void createNodes(DefaultMutableTreeNode top) {
    userList = new ArrayList() ;
    for(int i=0;i<10;i++) {
        User u1 = new User("User " + (i+1));
        u1.setStatus("Online");
         top.add(u1);
         userList.add(u1);
    }
}

private static void createAndShowGUI() {

    JFrame frame = new JFrame("TreeDemo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //Add content to the window.
    JTreeDemo jTreeDemo = new JTreeDemo();
    frame.add(jTreeDemo);
    frame.pack();
    frame.setVisible(true);
    // update status randomly
    Thread thread = new Thread(jTreeDemo);
    thread.start();
}

 public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });
 }
}

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

Результат:
enter image description here


Edit:
1. Исходя из предложения, я удалил reload(node) и добавил перезагрузку дерева.

Ответ 2

Возможно, если вы используете "nodeChanged()" вместо "reload()", вы получите желаемый эффект.

В классе DefaultTreeModel существует множество методов, которые вызывают изменение и переделку различных частей дерева. Существуют также другие методы в DefaultTreeModel, которые только приводят к перерисовке.

Вы упомянули 'reload (node)' и прокомментировали, что он вызывает сбой дерева при его вызове. "перезагрузка" полностью перерисовывает все поддеревье, начиная с этого node. (Но если этот node не отображается, он ничего не меняет.) Это называется "изменение структуры".

'insertNodeInto()' и 'removeNodeFromParent()' изменить древовидную структуру, добавив или удалив node, а затем перерисуя.

Я думаю, что "nodeChanged()" - это тот, который вам нужен, поскольку он просто уведомляет модель о том, что что-то изменилось в node, что приведет к ее отображению по-разному. Возможно, отображаемый текст теперь отличается от отображаемого. Возможно, вы изменили объект пользователя в node. Это когда вы вызываете 'nodeChanged()' на node.

Вы должны попробовать 'nodeChanged()' вместо вызова reload() в своём сворачиваемом коде и в приведенной в примере программе vels4j. Это может позаботиться о проблеме.

Обратите внимание, что есть еще два семейства методов в DefaultTreeModel, которые используются в других случаях:

Эти методы работают с узлами дерева и используют путь дерева, чтобы определить, где произошло изменение. Они не меняют структуры данных, лежащие в основе дерева, но уведомляют модель о том, что что-то изменилось, чтобы она могла уведомить слушателей, которые действительно перерисовывают вещи или иным образом реагируют на изменения.

nodesWereInserted() nodesWereRemovde() nodesChanged() nodeStructureChanged()

Существует также набор методов fire...(), которые используются внутренне для DefaultTreeModel и любых подклассов, которые вы можете создать. Они просто уведомляют слушателей о том, что что-то изменилось. Обратите внимание, что они защищены.

Ответ 3

Легко, если узлы содержат уникальные в дереве объекты и реализовали метод equals и hashCode (например, вы показываете строки или объекты с уникальным идентификатором из базы данных). Прежде всего, вы перебираете все расширенные узлы и сохраняете объекты из узлов в наборе. Затем вы выполняете обновление модели. После обновления вы перебираете все узлы, и если они находятся в наборе, вы разворачиваете node в дереве.
Если узлы не уникальны - вам нужно сохранить в наборе полный путь к дереву (например, как список) и проверить его после обновления, чтобы развернуть узлы.
Если объекты не имеют ни equals, ни hashCode (оба эти метода должны быть реализованы) - этот вариант не может быть использован.

Ответ 4

Только для записи (я проголосовал за Lee Meador), DefaultTreeModel # nodeChanged (javax.swing.tree.TreeNode) - это способ перейти:

public class TestFrame extends JFrame {

    public TestFrame() {
        //create gui with simple jtree (and DefaultTreeModel)
        JButton changeBtn = new JButton();
        final JTree jTree = new JTree();
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        changeBtn.setText("update selected node");
        getContentPane().add(changeBtn, java.awt.BorderLayout.PAGE_END);
        DefaultMutableTreeNode treeNode1 = new DefaultMutableTreeNode("root");
        DefaultMutableTreeNode treeNode2 = new DefaultMutableTreeNode("blue");
        treeNode1.add(treeNode2);
        treeNode2 = new DefaultMutableTreeNode("violet");
        DefaultMutableTreeNode treeNode3 = new DefaultMutableTreeNode("red");
        treeNode2.add(treeNode3);
        treeNode3 = new DefaultMutableTreeNode("yellow");
        treeNode2.add(treeNode3);
        treeNode1.add(treeNode2);
        jTree.setModel(new DefaultTreeModel(treeNode1));
        getContentPane().add(jTree, BorderLayout.CENTER);
        pack();
        //add listener to button, to change selected node on button click
        changeBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                DefaultMutableTreeNode dmt = (DefaultMutableTreeNode)jTree.getSelectionPath().getLastPathComponent();
                //update content/representation of selected node
                dmt.setUserObject("My update: " + new Date());
                //nodeChanged
                ((DefaultTreeModel) jTree.getModel()).nodeChanged(dmt);
            }
        });
    }

    public static void main(String args[]) {

        EventQueue.invokeLater(new Runnable() {
            public void run() {
                new TestFrame().setVisible(true);
            }
        });
    }
}

Ответ 5

Для полноты @kleopatra предложил, что вы можете использовать valueForPathChanged() из Модели.

По какой-то причине он работал лучше для меня (nodeChanged() did not). У меня есть пользовательский TreeCellRenderer.