How do I auto-expand a JTree when setting a new TreeModel?

29,503

Solution 1

I had a similar problem.

Your solution (as posted https://stackoverflow.com/a/15211697/837530) seemed to work for me only for the top level tree nodes.

But I needed to expand all the a descendants node. So I solved it with the following recursive method:

private void expandAllNodes(JTree tree, int startingIndex, int rowCount){
    for(int i=startingIndex;i<rowCount;++i){
        tree.expandRow(i);
    }

    if(tree.getRowCount()!=rowCount){
        expandAllNodes(tree, rowCount, tree.getRowCount());
    }
}

which is invoked with

expandAllNodes(tree, 0, tree.getRowCount());

where, tree is a JTree.

Unless someone has a better solution.

Solution 2

The following worked for me (called after setting the new model):

for (int i = 0; i < tree.getRowCount(); i++) {
    tree.expandRow(i);
}

Solution 3

There's also this non-recursive version.

private void expandAllNodes(JTree tree) {
    int j = tree.getRowCount();
    int i = 0;
    while(i < j) {
        tree.expandRow(i);
        i += 1;
        j = tree.getRowCount();
    }
}

Solution 4

this worked for me..

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Enumeration;

public class JTreeNodeAutoExpandCollapse extends JFrame {
    public JTreeNodeAutoExpandCollapse() throws HeadlessException {
        initializeUI();
    }

    private void initializeUI() {
        setSize(200, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
        DefaultMutableTreeNode chapter1 = new DefaultMutableTreeNode("Chapter 1");
        DefaultMutableTreeNode sub1 = new DefaultMutableTreeNode("1.1");
        DefaultMutableTreeNode sub2 = new DefaultMutableTreeNode("1.2");
        DefaultMutableTreeNode sub3 = new DefaultMutableTreeNode("1.3");
        DefaultMutableTreeNode sub31 = new DefaultMutableTreeNode("1.3.1");
        DefaultMutableTreeNode sub32 = new DefaultMutableTreeNode("1.3.2");

        root.add(chapter1);
        chapter1.add(sub1);
        chapter1.add(sub2);
        chapter1.add(sub3);
        sub3.add(sub31);
        sub3.add(sub32);

        final JTree tree = new JTree(root);
        expandTree(tree, false);

        JScrollPane pane = new JScrollPane(tree);
        pane.setPreferredSize(new Dimension(200, 200));

        JPanel buttonPanel = new JPanel(new BorderLayout());
        JButton expandAll = new JButton("Expand All");
        expandAll.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                expandTree(tree, true);
            }
        });

        JButton collapseAll = new JButton("Collapse All");
        collapseAll.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                expandTree(tree, false);
            }
        });

        buttonPanel.add(expandAll, BorderLayout.WEST);
        buttonPanel.add(collapseAll, BorderLayout.EAST);

        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(pane, BorderLayout.CENTER);
        getContentPane().add(buttonPanel, BorderLayout.SOUTH);
    }

    private void expandTree(JTree tree, boolean expand) {
        TreeNode root = (TreeNode) tree.getModel().getRoot();
        expandAll(tree, new TreePath(root), expand);
    }

    private void expandAll(JTree tree, TreePath path, boolean expand) {
        TreeNode node = (TreeNode) path.getLastPathComponent();

        if (node.getChildCount() >= 0) {
            Enumeration enumeration = node.children();
            while (enumeration.hasMoreElements()) {
                TreeNode n = (TreeNode) enumeration.nextElement();
                TreePath p = path.pathByAddingChild(n);

                expandAll(tree, p, expand);
            }
        }

        if (expand) {
            tree.expandPath(path);
        } else {
            tree.collapsePath(path);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new JTreeNodeAutoExpandCollapse().setVisible(true);
            }
        });
    }
}
Share:
29,503
sdasdadas
Author by

sdasdadas

Updated on March 18, 2020

Comments

  • sdasdadas
    sdasdadas about 4 years

    I have a custom JTree and a custom JModel; I would for the JTree to "auto-expand" when I give it a new model. At the moment, it simply collapse all the nodes to the root.

    Here is an example:

    private class CustomTree extends JTree {
    
        @Override
        public boolean isExpanded(TreePath path) {
            return ((Person) path.getLastPathComponent).hasChildren();
    
    }
    
    private class CustomTreeModel extends TreeModel {
    
        // ... omitting various implementation details
    
        @Override
        public boolean isLeaf(Object object) {
            return !((Person) object).hasChildren();
        }
    
    }
    
    Model model = new Model();
    Person bob = new Person();
    Person alice = new Person();
    bob.addChild(alice);
    model.setRoot(bob);
    JTree tree = new CustomTree(new CustomTreeModel(model));
    

    At this point, the tree correctly displays:

    - BOB
      - ALICE
    

    where Alice is a child of Bob (both in the data and in the visual tree)

    However, if I call:

    tree.setModel(new CustomTreeModel(model));
    

    everything is collapsed:

    + BOB
    

    Is there a way to "auto-expand" everything in the tree when setting a new model?

    • mKorbel
      mKorbel about 11 years
      no ideas from this code and description, all notifiers & listener go away after model is changed,
    • sdasdadas
      sdasdadas about 11 years
      @mKorbel I believe my answer below solves my problem.
    • mKorbel
      mKorbel about 11 years
      :-) ............... do not use index by int, you can lost this index, have to store all accesible about node inc. Object value (my view)
    • Andrew Thompson
      Andrew Thompson about 11 years
      Expand on setting a new model. For better help sooner, post an SSCCE.
    • sdasdadas
      sdasdadas about 11 years
      @mKorbel I'm sorry I don't quite understand - I can lose the tree.getRowCount() index? How?
    • sdasdadas
      sdasdadas about 11 years
      @AndrewThompson tree.setModel(new CustomTreeModel(model))
    • mKorbel
      mKorbel about 11 years
      models can be different, tree.getRowCount() <> with old model, or <> structure of nodes, or <> value from one, two or all elements, if yes then don't recreate a model use implemented menthods and notifiers, have you issue with nodeChanged, post an SSCCE, where you replace an new model with different structure, then this question make me sence
  • rypel
    rypel about 9 years
    @all: this solution is sufficient, here's no need for recursion
  • Matthieu
    Matthieu over 7 years
    As for "why": it works because expandRow() will show more rows, making getRowCount() increase after each expansion. Trying to be smart by storing n = getRowCount() and for (i=0; i<n; i++) will not work.
  • Itamar Mushkin
    Itamar Mushkin about 4 years
    Please add some explanation to your code. Also, since this is an old question, check that your answer isn't already contained in another answer.
  • JPS
    JPS almost 4 years
    see sdasdadas answer below for a 3 line solution without recursion.
  • Java-Dev
    Java-Dev over 3 years
    @Alexander, in expandTree() method int row = 1; should be start from 0(int row = 0;) else the first node after Root node will not be expand.
  • rjmunro
    rjmunro over 2 years
    You're saving the rowCount, then expanding that many lines. But by the time you've expanded them, more lines are added. If you change i<rowCount in your for statement to i<tree.getRowCount(), it will keep expanding from the top, and won't end until it has reached all the expanded rows, no need for recursion.