Развернуть Свернуть Развернуть Проблема с отложенной загрузкой JTree

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

Данные поступают из БД, и мы запускаем запрос к базе данных, чтобы заполнить дочерние узлы. Реализован TreeExpansionListener и использование переопределенной реализации метода treeExpanded. При расширении я удаляю все дочерние узлы для выбранного узла, делаю запрос к БД и добавляю записи как дочерние к выбранному узлу. Перед добавлением любого узла в дерево к узлу добавляется фиктивный дочерний элемент. Работа с DefaultMutableTreeNode.

Пока все хорошо, все работало нормально, как и ожидалось.

Проблема 1. Как вы, это вызов БД для каждого расширения, поэтому, если узел свернут и снова расширится, я буду выполнять поездку по БД и снова обрабатывать снова... Идея состоит в том, чтобы не загружать узлы свежими, если они уже расширены ...

Проблема 2. Если бы мне пришлось принудительно обновить, то есть перезагрузить дерево и сохранить состояние расширения. Сейчас это в рабочем состоянии ... Как я могу добиться того же, а также исправить проблему 1 выше?

Ценим любую помощь в этом.


person newbie    schedule 25.02.2013    source источник
comment
Если узел свернут, не очищайте данные. Когда он снова расширится, проверьте, есть ли в узле уже данные...   -  person vikingsteve    schedule 26.02.2013
comment
Я переопределяю метод treeCollapsed и ничего в нем не делаю. Как остановить удаление данных.   -  person newbie    schedule 26.02.2013
comment
В treeExpanded проверьте, содержит ли родительский узел дочерние элементы. Если это так. Ничего не перезагружать   -  person MadProgrammer    schedule 26.02.2013


Ответы (2)


Как объяснил MadProgrammer, вот небольшой демонстрационный пример, показывающий, как JTree динамически загружает данные по мере расширения дерева на узлах (и с приятной полосой загрузки в качестве бонуса):

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.Transient;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;

public class TestJTree {

    public static class MyTreeNode extends DefaultMutableTreeNode {

        private boolean loaded = false;

        private int depth;

        private final int index;

        public MyTreeNode(int index, int depth) {
            this.index = index;
            this.depth = depth;
            add(new DefaultMutableTreeNode("Loading...", false));
            setAllowsChildren(true);
            setUserObject("Child " + index + " at level " + depth);
        }

        private void setChildren(List<MyTreeNode> children) {
            removeAllChildren();
            setAllowsChildren(children.size() > 0);
            for (MutableTreeNode node : children) {
                add(node);
            }
            loaded = true;
        }

        @Override
        public boolean isLeaf() {
            return false;
        }

        public void loadChildren(final DefaultTreeModel model, final PropertyChangeListener progressListener) {
            if (loaded) {
                return;
            }
            SwingWorker<List<MyTreeNode>, Void> worker = new SwingWorker<List<MyTreeNode>, Void>() {
                @Override
                protected List<MyTreeNode> doInBackground() throws Exception {
                    // Here access database if needed
                    setProgress(0);
                    List<MyTreeNode> children = new ArrayList<TestJTree.MyTreeNode>();
                    if (depth < 5) {
                        for (int i = 0; i < 5; i++) {
                            // Simulate DB access time
                            Thread.sleep(300);
                            children.add(new MyTreeNode(i + 1, depth + 1));
                            setProgress((i + 1) * 20);
                        }
                    } else {
                        Thread.sleep(1000);
                    }
                    setProgress(0);
                    return children;
                }

                @Override
                protected void done() {
                    try {
                        setChildren(get());
                        model.nodeStructureChanged(MyTreeNode.this);
                    } catch (Exception e) {
                        e.printStackTrace();
                        // Notify user of error.
                    }
                    super.done();
                }
            };
            if (progressListener != null) {
                worker.getPropertyChangeSupport().addPropertyChangeListener("progress", progressListener);
            }
            worker.execute();
        }

    }

    protected void initUI() {
        JFrame frame = new JFrame(TestJTree.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        MyTreeNode root = new MyTreeNode(1, 0);
        final DefaultTreeModel model = new DefaultTreeModel(root);
        final JProgressBar bar = new JProgressBar();
        final PropertyChangeListener progressListener = new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                bar.setValue((Integer) evt.getNewValue());
            }
        };
        JTree tree = new JTree() {
            @Override
            @Transient
            public Dimension getPreferredSize() {
                Dimension preferredSize = super.getPreferredSize();
                preferredSize.width = Math.max(400, preferredSize.width);
                preferredSize.height = Math.max(400, preferredSize.height);
                return preferredSize;
            }
        };
        tree.setShowsRootHandles(true);
        tree.addTreeWillExpandListener(new TreeWillExpandListener() {

            @Override
            public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
                TreePath path = event.getPath();
                if (path.getLastPathComponent() instanceof MyTreeNode) {
                    MyTreeNode node = (MyTreeNode) path.getLastPathComponent();
                    node.loadChildren(model, progressListener);
                }
            }

            @Override
            public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {

            }
        });
        tree.setModel(model);
        root.loadChildren(model, progressListener);
        frame.add(new JScrollPane(tree));
        frame.add(bar, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestJTree().initUI();
            }
        });
    }
}
person Guillaume Polet    schedule 25.02.2013
comment
Спасибо за демо... Мне удалось решить проблему с помощью комментариев MadProgrammer.... - person newbie; 28.02.2013
comment
Не могли бы вы предложить, как я могу обновить это дерево (заполненное с помощью отложенной загрузки), чтобы все расширенные узлы были повторно извлечены, а состояние раскрытия сохранено. Я могу сохранить текущее расширенное состояние, но проблема заключается в ExpandRow(), который расширяет дерево по номеру строки, будучи динамическим, он в конечном итоге открывает какую-то другую строку... ExpandPath здесь правильный выбор, но по какой-то причине не работает: ( - person newbie; 28.02.2013
comment
@newbie Используйте для этого TreePath: извлеките их все (укажите корневой TreePath в качестве параметра), используя javax.swing.JTree.getExpandedDescendants(TreePath), а затем установите их обратно с помощью javax.swing.JTree.expandPath(TreePath). В противном случае попробуйте добавить/удалить/обновить только узлы, не изменяя другие узлы. - person Guillaume Polet; 01.03.2013
comment
Привет, наконец-то мне удалось сохранить узлы расширенными, сопоставив строку с TreePath, а затем восстановив ее, и я столкнулся с другой проблемой. При расширении выбор дерева смещается к последнему расширенному узлу, а контекст (узел, на котором было вызвано обновление) больше не выделяется. Даже если я сохраню TreePath для узла контекста перед вызовом обновления, а затем установлю путь выбора в конце обновления, контекст будет потерян и будет указывать на последний расширенный узел. Пожалуйста, предложите - person newbie; 04.03.2013
comment
@newbie Пожалуйста, задайте еще один вопрос. Комментарии предназначены не для того, чтобы поставить дополнительный вопрос, а только для уточнения исходного вопроса/ответа. - person Guillaume Polet; 04.03.2013
comment
разместил здесь новый Questio.... stackoverflow.com/questions/15209287/ - person newbie; 05.03.2013
comment
@GuillaumePolet: почему вы используете TreeWillExpandListener вместо TreeExpansionListener? Документ Java говорит: слушатель tree-will-expand предотвращает расширение или свертывание узла дерева. Чтобы получать уведомления сразу после расширения или свертывания, вместо этого следует использовать прослушиватель расширения дерева. и прослушиватель расширения дерева обнаруживает, когда расширение или свертывание уже произошло. В общем, вы должны реализовать прослушиватель расширения дерева, если вам не нужно предотвратить расширение или свертывание. - person Oswin; 18.08.2014

Как только узел заполнен, просто не заполняйте его повторно. На treeExpanded просто проверьте, есть ли у родительского узла какие-либо дочерние элементы, если это так, ничего не перезагружайте.

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

person MadProgrammer    schedule 25.02.2013
comment
Спасибо за демо... Мне удалось решить проблему с помощью комментариев MadProgrammer.. - person newbie; 28.02.2013