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

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

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

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


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

Переопределил вопрос для уточнения.


person David B    schedule 03.09.2012    source источник
comment
Вы должны обновлять свой JTree только из потока пользовательского интерфейса - если вы это сделаете, нет необходимости ничего синхронизировать.   -  person assylias    schedule 06.09.2012
comment
Не будет ли проблемой, если JTree может обновляться несколько раз в течение 60-х годов?   -  person David B    schedule 06.09.2012
comment
Если все выполняется в одном потоке (что и должно быть), все операции будут последовательными. Худшее, что может случиться, это то, что он может работать медленно, если он будет обновляться слишком часто, но обновления будут запускаться одно за другим.   -  person assylias    schedule 06.09.2012
comment
Верно, но когда я вызываю DefaultTreeModel.reload() для модели моего JTree, узлы рушатся, как я могу обновить конкретный узел, не разрушив ни один из других узлов с дочерними элементами?   -  person David B    schedule 06.09.2012
comment
Возможно, проверьте этот пост и stackoverflow.com/questions/10435133/   -  person assylias    schedule 06.09.2012
comment
Извините за задержку ответа. Тема с комментариями не решила эту проблему. Я обновил свой вопрос для дальнейших разъяснений.   -  person David B    schedule 20.01.2013
comment
Все еще не понимаю, почему обновление узла может быть проблемой - что/почему/как именно приводит к обновлению?   -  person kleopatra    schedule 20.01.2013
comment
Я столкнулся с раздражающим нежелательным коллапсом узлов, когда я вызываю treeStructureChanged() вместо treeNodesChanged(). Подробнее см. в этом ответе, особенно в его последней части, с примерами изменения узла. Но я не работал с DefaultXXX материалом. В любом случае, покажите нам свой код.   -  person Dmitry Frank    schedule 20.01.2013


Ответы (4)


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

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

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

Вывод:
введите здесь описание изображения


Изменить:
1. По предложению я удалил reload(node) и добавил перезагрузку модели дерева.

person vels4j    schedule 20.01.2013
comment
-1 а) обновление модели из EDT б) перезагрузка вместо запуска измененного в) (незначительная проблема) визуальное оформление внутри узла - не переопределяйте toString для просмотра, это задача средства визуализации - person kleopatra; 22.01.2013
comment
@kleopatra спасибо за ваши очки. Я написал, насколько мне известно, поэтому я упомянул, что мой ответ поможет вам решить проблему. Могу ли я узнать, что такое EDT? - person vels4j; 22.01.2013

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

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

Вы упомянули «перезагрузку (узел)» и отметили, что это приводит к краху дерева, когда вы его вызываете. 'reload' приводит к полной перерисовке всего поддерева, начиная с этого узла. (Но если этот узел не виден, он ничего не меняет.) Это называется «изменением структуры».

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

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

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

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

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

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

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

person Lee Meador    schedule 21.01.2013
comment
+1 DefaultTreeModel#nodeChanged(javax.swing.tree.TreeNode) — правильный путь. - person chromanoid; 23.01.2013
comment
Можете ли вы предоставить SSCCE, предоставленный @vels4j, чтобы я мог принять ваш ответ. - person David B; 23.01.2013
comment
Вы можете взять код @vels4j и изменить вызов reload() на nodeChanged(). Я не думаю, что что-то еще нужно. Я надеюсь, что есть способ разделить заслугу, поскольку он/она проделал большую часть работы, чтобы дать вам ваш ответ. Я просто добавил улучшение, чтобы он не рушился и не думал, что произошло изменение структуры. - person Lee Meador; 24.01.2013

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

person Sergiy Medvynskyy    schedule 23.01.2013
comment
Верно, но это может занять некоторое время ЦП, поскольку мы говорим об итерации по узлам каждый раз, когда должно быть выполнено обновление. - person David B; 25.01.2013

Просто для протокола (я голосовал за Ли Мидора), 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);
            }
        });
    }
}
person chromanoid    schedule 27.01.2013