Hm. Falls ich das richtig verstanden habe, existiert der Teil des JTrees ja noch gar nicht, in dem Moment, wo es ausgeklappt werden soll. (Also, dieser Teil wird ja erst durch den SelectionListener erstellt …).
(Ich würde da eher einen TreeWillExpandListener
verwenden, aber … vielleicht gibt es für den SelectionListener ja einen Grund…)
Zugegeben, mit dem FileSystemView
hatte ich bisher noch nicht wirklich was gemacht. Ich war dann etwas irritiert, zu sehen, dass er bei mir „Desktop“ als Root zurückgegeben hat, aber auch als Home-Directory - das macht irgendwie keinen Sinn…
Wie auch immer, es gibt bei der genauen Umsetztung sicher einige Freiheitsgrade, aber ich hab’ mal was gebastelt, was „bei mir funktioniert“ - vielleicht ist doch der eine oder andere hilfreiche Schnipsel drin…
package bytewelt;
import java.awt.BorderLayout;
import java.io.File;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
public class FileTreeExpandTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTreeModel fileTreeModel = createFileTreeModel();
JTree tree = new JTree(fileTreeModel);
JScrollPane scrollPane = new JScrollPane(tree);
tree.addTreeWillExpandListener(new TreeWillExpandListener()
{
@Override
public void treeWillExpand(TreeExpansionEvent event)
throws ExpandVetoException
{
TreePath path = event.getPath();
DefaultMutableTreeNode lastNode =
(DefaultMutableTreeNode)path.getLastPathComponent();
Object userObject = lastNode.getUserObject();
File file = (File)userObject;
List<File> subdirectories = getSubdirectories(file);
System.out.println("treeWillExpand at " + lastNode
+ ", validating children for " + subdirectories);
validateChildren(lastNode, subdirectories);
}
@Override
public void treeWillCollapse(TreeExpansionEvent event)
throws ExpandVetoException
{
// Not used
}
});
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JButton expandButton = new JButton("Expand Home");
expandButton.addActionListener(e -> expandHome(tree));
frame.getContentPane().add(expandButton, BorderLayout.SOUTH);
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static void expandHome(JTree fileTree)
{
File homePath = new File("C:/Users/User/");
List<File> pathObjects = createPathFromRoot(homePath);
System.out.println("Expanding "+pathObjects);
TreeModel treeModel = fileTree.getModel();
DefaultTreeModel defaultTreeModel = (DefaultTreeModel) treeModel;
TreePath treePath = validateTreePath(defaultTreeModel, pathObjects);
fileTree.expandPath(treePath);
fileTree.setSelectionPath(treePath);
}
// Create a list containing the Files from the root of the
// file system, up to the given file
private static List<File> createPathFromRoot(File file)
{
Deque<File> list = new LinkedList<File>();
File current = file;
while (current != null)
{
list.addFirst(current);
current = FILE_SYSTEM_VIEW.getParentDirectory(current);
}
return new ArrayList<File>(list);
}
// Make sure that the given tree model contains a path with
// the given user objects, creating the nodes if necessary,
// and return this path
private static TreePath validateTreePath(
DefaultTreeModel treeModel, List<?> objects)
{
DefaultMutableTreeNode rootNode =
(DefaultMutableTreeNode)treeModel.getRoot();
DefaultMutableTreeNode currentNode = rootNode;
TreePath treePath = new TreePath(currentNode);
for (int i=0; i<objects.size(); i++)
{
Object object = objects.get(i);
DefaultMutableTreeNode nextNode =
validateChild(currentNode, object);
currentNode = nextNode;
treePath = treePath.pathByAddingChild(currentNode);
}
return treePath;
}
// Make sure that the given node has a child with the given
// user object, creating it if necessary, and return the child
private static DefaultMutableTreeNode validateChild(
DefaultMutableTreeNode node, Object userObject)
{
int n = node.getChildCount();
for (int i = 0; i < n; i++)
{
DefaultMutableTreeNode child =
(DefaultMutableTreeNode)node.getChildAt(i);
Object childUserObject = child.getUserObject();
if (Objects.equals(childUserObject, userObject))
{
return child;
}
}
DefaultMutableTreeNode newChild =
new DefaultMutableTreeNode(userObject);
node.add(newChild);
return newChild;
}
private static FileSystemView FILE_SYSTEM_VIEW =
FileSystemView.getFileSystemView();
private static DefaultTreeModel createFileTreeModel()
{
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
File[] fileSystemRoots = FILE_SYSTEM_VIEW.getRoots();
for (File rootPath : fileSystemRoots)
{
DefaultMutableTreeNode node = new DefaultMutableTreeNode(rootPath);
root.add(node);
List<File> subdirectories = getSubdirectories(rootPath);
validateChildren(node, subdirectories);
}
return new DefaultTreeModel(root, true);
}
// If the given file is a directory, return a list of all subdirectories
private static List<File> getSubdirectories(File file)
{
List<File> subdirectories = new ArrayList<File>();
if (file.isDirectory())
{
File[] subDirs = FILE_SYSTEM_VIEW.getFiles(file, true);
for (File subDir : subDirs)
{
if (subDir.isDirectory())
{
subdirectories.add(subDir);
}
}
}
return subdirectories;
}
// Make sure that the given node has children
// with all the given user objects
private static boolean validateChildren(
DefaultMutableTreeNode node, Iterable<?> childUserObjects)
{
boolean changed = false;
Set<Object> existing = getChildUserObjects(node);
for (Object childUserObject : childUserObjects)
{
if (!existing.contains(childUserObject))
{
node.add(new DefaultMutableTreeNode(childUserObject));
changed = true;
}
}
return changed;
}
// Returns a set of the user objects of all children of the given node
private static Set<Object> getChildUserObjects(Object nodeObject)
{
Set<Object> childUserObjects = new LinkedHashSet<Object>();
if (!(nodeObject instanceof DefaultMutableTreeNode))
{
// Warning here
return childUserObjects;
}
DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeObject;
int n = node.getChildCount();
for (int i = 0; i < n; i++)
{
DefaultMutableTreeNode child =
(DefaultMutableTreeNode)node.getChildAt(i);
Object childUserObject = child.getUserObject();
childUserObjects.add(childUserObject);
}
return childUserObjects;
}
}