【问题标题】:Expand Collapse Expand Issue with JTree Lazy loading展开 折叠 展开 JTree 延迟加载的问题
【发布时间】:2023-03-06 21:34:01
【问题描述】:

我已经使用延迟加载实现了一棵树。第一级节点在树创建时创建,子节点仅在用户展开任何特定节点时创建。

数据来自数据库,我们向数据库发起查询以填充子节点。实现 TreeExpansionListener 并使用 treeExpanded 方法的重写实现。在扩展时,我删除所选节点的所有子节点,进行数据库查询并将记录作为子节点添加到所选节点。在将任何节点添加到树之前,会向该节点添加一个虚拟子节点。使用 DefaultMutableTreeNode。

到目前为止一切都很好,按预期运行良好。

问题 1 - 就像您一样,每次扩展都会调用数据库,所以如果一个节点被折叠并再次扩展,我将进行一次数据库旅行并再次处理......这个想法是不要重新加载节点如果已经扩展...

问题 2 - 如果我必须强制刷新,即重新加载树并保持扩展状态。现在处于工作状态...我怎样才能实现与上述问题 1 相同的修复?

感谢您对此的任何帮助。

【问题讨论】:

  • 如果节点折叠,不要清除数据。再次展开时,检查节点是否已经有数据...
  • 我重写了 treeCollapsed 方法并且在其中什么也不做。如何阻止数据擦除。
  • 在 treeExpanded 上,检查父节点是否包含子节点。如果是这样。不重新加载任何东西

标签: java swing jtree expand collapse


【解决方案1】:

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

【讨论】:

  • 感谢您的演示......我能够使用 MadProgrammer cmets 解决问题......
  • 您能否建议我如何刷新此树(使用延迟加载填充),以便重新获取所有扩展节点并保留扩展状态。我能够存储当前的扩展状态,但问题在于 expandRow() 它按行号扩展树,它是动态的,它最终会打开其他行... expandPath 在这里是正确的选择,但由于某种原因无法正常工作: (
  • @newbie 为此使用TreePath:使用javax.swing.JTree.getExpandedDescendants(TreePath) 检索它们(提供根TreePath 作为参数),然后使用javax.swing.JTree.expandPath(TreePath) 将它们设置回来。否则,尝试只添加/删除/更新节点而不修改其他节点。
  • 嗨,我终于能够通过将行映射到 TreePath 然后恢复它来保持节点扩展,但我遇到了另一个问题。展开时,树选择转移到最后展开的节点,并且不再选择上下文(调用刷新的节点)。即使我在调用 refresh 之前为上下文节点保存 TreePath,然后在 refresh 结束时设置选择路径,上下文也会丢失并指向最后一个展开的节点。请建议
  • @newbie 请发布另一个问题。评论不是为了提出更多问题,只是为了澄清原始问题/答案。
【解决方案2】:

一旦一个节点被填充,简单的不要重新填充它。在treeExpanded 上,只需检查父节点是否有任何子节点,如果有,则不要重新加载任何内容。

您需要提供刷新父节点的能力。我通常使用JPopupMenu 来执行此操作。

【讨论】:

  • 感谢您的演示......我能够使用 MadProgrammer cmets 解决问题......
猜你喜欢
  • 2011-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
  • 1970-01-01
  • 2015-01-13
  • 2017-03-09
  • 2013-06-06
相关资源
最近更新 更多