Expand Collapse Expand Issue with JTree Lazy loading

后端 未结 2 1229
半阙折子戏
半阙折子戏 2021-01-01 02:02

I have implemented a tree using Lazy Loading. The 1st level nodes are created at the time of tree creation, where as the child nodes are created only when a user expands any

相关标签:
2条回答
  • 2021-01-01 02:53

    As explained by MadProgrammer, here is a small demo example showing a JTree loading dynamically data as the tree expands on nodes (and with a nice loading bar as a bonus):

    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();
                }
            });
        }
    }
    
    0 讨论(0)
  • 2021-01-01 03:02

    Once a node has populated, simple don't repopulate it. On treeExpanded, simply check to see of the parent node has any children, if it does, don't reload anything.

    You will need to supply the ability to refresh a parent node. I normally do this with a JPopupMenu.

    0 讨论(0)
提交回复
热议问题