Configuring a TreeView which scans Local fie system to only include folders which have a file type

此生再无相见时 提交于 2019-12-23 22:11:36

问题


Okay so I used the second block of code from this website http://www.java2s.com/Tutorials/Java/JavaFX/0660__JavaFX_Tree_View.htm where it states "The following code create a dynamic tree from the local file system"

I do not understand how this code works in order to customise it to my needs. Particularly the overriding methods, there did not seem to be a place where I could add in "only add folders down to the sub directory which contain mp3 files". I reckon it's likely going to require something more complex such as something that goes through and removes folders. I'm honestly not sure.

I have tried to use this code in my program which is to show mp3 files. The idea was to have two treeViews side by side, the left side shows the hierarchy of folders to the folders which have mp3 files in them (and do not shows other folders which do not have mp3 files in them) and the right side shows files which are of only mp3 file type in those folders. There is a screenshot further down.

This is the code I have so far which returns a TreeView in a VBox. There are two segments of code which are commented out. The first is due to the fact that java: search file according to its name in directory and subdirectories does not wish to search my C: drive. (I do not know why). So I changed it to only scan my D: (Partition drive). The second is from the webpage where I got the main segment of code. This code was moved to an outer class which handles. As well as a cheeky bit of code to handle more than one drive.

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
import org.apache.commons.io.FileUtils;

/**
 * https://stackoverflow.com/questions/6251762/java-search-file-according-to-its-name-in-directory-and-subdirectories
 * https://stackoverflow.com/questions/26690247/how-to-make-directories-expandable-in-javafx-treeview
 * http://www.java2s.com/Tutorials/Java/JavaFX/0660__JavaFX_Tree_View.htm
 *
 * @author Scorchgid
 */
public class FolderTreeView {

    int x = 0;
    private final String fileName = ".mp3";
    private MainView mainView;
    private TreeView<File> treeViewFile = new TreeView<>();

    public TreeView<File> getTreeViewFile() {
        return treeViewFile;
    }

    public void setTreeViewFile(TreeView<File> treeViewFile) {
        this.treeViewFile = treeViewFile;
    }

    public VBox treeStack() throws IOException {
        VBox vbox = new VBox();
        File[] drives = File.listRoots();
        ArrayList<File> fileListing;
        /*for (File dir : drives) {
            System.out.println(dir.toString());
            fileListing = restrictingList(dir);
        }*/
        fileListing = restrictingList(new File("D:\\"));

        ArrayList<TreeItem> treeItems = new ArrayList<>();
        for (File dir : drives) {
            //System.out.println(dir.toString());
            treeItems.add(createNode(dir));
        }
        TreeView<File> tree = proxyCreateNode(treeItems);
        vbox.getChildren().add(tree);

        return vbox;
    }

    // https://stackoverflow.com/questions/22260032/set-two-root-nodes-for-treeview
    public TreeView<File> proxyCreateNode(ArrayList<TreeItem> arrayListTreeItem) {
        TreeItem<File> proxyItem = new TreeItem<>();
        proxyItem.setExpanded(true);
        for (TreeItem<File> item : arrayListTreeItem) {
            proxyItem.getChildren().addAll(item);
        }
        TreeView<File> tree = new TreeView<>(proxyItem);
        tree.setShowRoot(false);
        return tree;
    }

    private ArrayList<File> restrictingList(File root) {
        ArrayList<File> fileArray = new ArrayList<>();        
        boolean recursive = true;        
        Collection files = FileUtils.listFiles(root, null, recursive);
        for (Iterator iterator = files.iterator(); iterator.hasNext();) {
            File file = (File) iterator.next();
            if (file.getName().endsWith(fileName)) {
                fileArray.add(file);              
            }
        }
        return fileArray;
    }

    /*    @Override
     public void start(Stage stage) {
     Scene scene = new Scene(new Group(), 300, 300);

     TreeItem<File> root = createNode(new File("c:/"));
     TreeView treeView = new TreeView<File>(root);

     vbox.getChildren().add(treeView);
     ((Group) scene.getRoot()).getChildren().add(vbox);

     stage.setScene(scene);
     stage.show();
     }
     */
    private TreeItem<File> createNode(final File f) {
        return new TreeItem<File>(f) {
            private boolean isLeaf;
            private boolean isFirstTimeChildren = true;
            private boolean isFirstTimeLeaf = true;

            @Override
            public ObservableList<TreeItem<File>> getChildren() {
                if (isFirstTimeChildren) {
                    isFirstTimeChildren = false;
                    super.getChildren().setAll(buildChildren(this));
                }
                return super.getChildren();
            }

            @Override
            public boolean isLeaf() {
                if (isFirstTimeLeaf) {
                    isFirstTimeLeaf = false;
                    File f = (File) getValue();
                    isLeaf = f.isFile();
                }
                return isLeaf;
            }

            private ObservableList<TreeItem<File>> buildChildren(
                    TreeItem<File> TreeItem) {
                File f = TreeItem.getValue();
                if (f == null) {
                    return FXCollections.emptyObservableList();
                }
                if (f.isFile()) {
                    return FXCollections.emptyObservableList();
                }
                File[] files = f.listFiles();
                if (files != null) {
                    ObservableList<TreeItem<File>> children = FXCollections
                            .observableArrayList();
                    for (File childFile : files) {
                        //System.out.println("Adding " + childFile.getAbsolutePath());
                        if (childFile.isDirectory()) {
                            children.add(createNode(childFile));
                        }
                    }
                    return children;
                }
                return FXCollections.emptyObservableList();
            }
        };
    }
}


回答1:


You can create the tree structure recursively. There are various mechanisms (please also note jewelsea's comment to your question), here's one:

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.function.Function;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class FolderTreeView extends Application {

    private static final String ROOT_FOLDER = "c:/music"; // TODO: change or make selectable

    @Override
    public void start(Stage primaryStage) throws IOException {

        // create root
        TreeItem<Path> treeItem = new TreeItem<Path>(Paths.get( ROOT_FOLDER));
        treeItem.setExpanded(true);

        // create tree structure
        createTree( treeItem);

        // sort tree structure by name
        treeItem.getChildren().sort( Comparator.comparing( new Function<TreeItem<Path>, String>() {
            @Override
            public String apply(TreeItem<Path> t) {
                return t.getValue().toString().toLowerCase();
            }
        }));

        // create components
        TreeView<Path> treeView = new TreeView<Path>(treeItem);
        StackPane root = new StackPane();
        root.getChildren().add(treeView);
        primaryStage.setScene(new Scene(root, 1024, 768));
        primaryStage.setTitle("Folder Tree View Example");
        primaryStage.show();

    }

    /**
     * Recursively create the tree
     * @param rootItem
     * @throws IOException
     */
    public static void createTree(TreeItem<Path> rootItem) throws IOException {

        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(rootItem.getValue())) {

            for (Path path : directoryStream) {

                TreeItem<Path> newItem = new TreeItem<Path>(path);
                newItem.setExpanded(true);

                rootItem.getChildren().add(newItem);

                if (Files.isDirectory(path)) {
                    createTree(newItem);
                }
            }
        }
    }


    public static void main(String[] args) {
        launch(args);
    }
}

Then you have a tree with all of your files. Since you intend to scan the entire tree structure from a given root path and keep it in memory, you may as well simply filter the tree. I took the code above and the filter code from rli's answer in this post and adapted it. It basically creates a filtered tree structure from the original structure.

Here's the full example code:

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.function.Function;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FolderTreeViewWithFilter extends Application {

    private static final String ROOT_FOLDER = "c:/music"; // TODO: change or make selectable

    TreeItem<FilePath> rootTreeItem;
    TreeView<FilePath> treeView;

    @Override
    public void start(Stage primaryStage) throws IOException {

        // root component
        VBox root = new VBox();

        // filter
        TextField filter = new TextField();
        filter.textProperty().addListener((observable, oldValue, newValue) -> filterChanged(newValue));

        // treeview
        treeView = new TreeView<FilePath>();
        VBox.setVgrow(treeView, Priority.ALWAYS);

        root.getChildren().addAll( filter, treeView);

        // stage
        primaryStage.setScene(new Scene(root, 1024, 768));
        primaryStage.setTitle("Folder Tree View With Filter Example");
        primaryStage.show();

        // create tree
        createTree();

        // show tree structure in tree view
        treeView.setRoot(rootTreeItem);
    }

    /**
     * Create original tree structure
     * @throws IOException
     */
    private void createTree() throws IOException {

        // create root
        rootTreeItem = createTreeRoot();

        // create tree structure recursively
        createTree( rootTreeItem);

        // sort tree structure by name
        rootTreeItem.getChildren().sort( Comparator.comparing( new Function<TreeItem<FilePath>, String>() {
            @Override
            public String apply(TreeItem<FilePath> t) {
                return t.getValue().toString().toLowerCase();
            }
        }));

    }

    /**
     * Iterate through the directory structure and create a file tree
     * @param rootItem
     * @throws IOException
     */
    public static void createTree(TreeItem<FilePath> rootItem) throws IOException {

        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(rootItem.getValue().getPath())) {

            for (Path path : directoryStream) {

                TreeItem<FilePath> newItem = new TreeItem<FilePath>( new FilePath( path));
                newItem.setExpanded(true);

                rootItem.getChildren().add(newItem);

                if (Files.isDirectory(path)) {
                    createTree(newItem);
                }
            }
        }
        // catch exceptions, e. g. java.nio.file.AccessDeniedException: c:\System Volume Information, c:\$RECYCLE.BIN
        catch( Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Create new filtered tree structure
     * @param root
     * @param filter
     * @param filteredRoot
     */
    private void filter(TreeItem<FilePath> root, String filter, TreeItem<FilePath> filteredRoot) {

        for (TreeItem<FilePath> child : root.getChildren()) {

            TreeItem<FilePath> filteredChild = new TreeItem<>( child.getValue());
            filteredChild.setExpanded(true);

            filter(child, filter, filteredChild );

            if (!filteredChild.getChildren().isEmpty() || isMatch(filteredChild.getValue(), filter)) {
                filteredRoot.getChildren().add(filteredChild);
            }

        }
    }

    /**
     * Comparator for tree filter
     * @param value
     * @param filter
     * @return
     */
    private boolean isMatch(FilePath value, String filter) {
        return value.toString().toLowerCase().contains( filter.toLowerCase()); // TODO: optimize or change (check file extension, etc)
    }

    /**
     * Show original tree or filtered tree depending on filter
     * @param filter
     */
    private void filterChanged(String filter) {
        if (filter.isEmpty()) {
            treeView.setRoot(rootTreeItem);
        }
        else {
            TreeItem<FilePath> filteredRoot = createTreeRoot();
            filter(rootTreeItem, filter, filteredRoot);
            treeView.setRoot(filteredRoot);
        }
    }

    /**
     * Create root node. Used for the original tree and the filtered tree.
     * Another option would be to clone the root.
     * @return
     */
    private TreeItem<FilePath> createTreeRoot() {
        TreeItem<FilePath> root = new TreeItem<FilePath>( new FilePath( Paths.get( ROOT_FOLDER)));
        root.setExpanded(true);
        return root;
    }

    /**
     * Wrapper for the path with overwritte toString method. We only want to see the last path part as tree node, not the entire path.
     */
    private static class FilePath {

        Path path;
        String text;

        public FilePath( Path path) {

            this.path = path;

            // display text: the last path part
            // consider root, e. g. c:\
            if( path.getNameCount() == 0) {
                this.text = path.toString();
            }
            // consider folder structure
            else {
                this.text = path.getName( path.getNameCount() - 1).toString();
            }

        }

        public Path getPath() {
            return path;
        }

        public String toString() {

            // hint: if you'd like to see the entire path, use this:
            // return path.toString();

            // show only last path part
            return text;

        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Simply enter search text in the text field, the tree will be filtered accordingly.

You may change the toString() method if you'd like to see the full path. Also, currently a substring is searched instead of the file extension. That's just for demo, adapting it for your needs is trivial.

Similar mechanism applies if you'd like to display parts of the tree in another tree view or list view.

Example if you'd like to get a tree structure with all nodes that contain a file with e. g. substring "mp3" in the text filter while not displaying the file itself in the tree, here's the modified version of the filter method:

/**
 * Create new filtered tree structure
 * @param root
 * @param filter
 * @param filteredRoot
 */
private void filter(TreeItem<FilePath> root, String filter, TreeItem<FilePath> filteredRoot) {

    for (TreeItem<FilePath> child : root.getChildren()) {

        TreeItem<FilePath> filteredChild = new TreeItem<>( child.getValue());
        filteredChild.setExpanded(true);

        filter(child, filter, filteredChild );

        boolean hasItem = false;
        for (TreeItem<FilePath> subChild: child.getChildren()) {
            if( isMatch( subChild.getValue(), filter)) {
                hasItem = true;
                break;
            }
        }

        if (!filteredChild.getChildren().isEmpty() || hasItem) {
            filteredRoot.getChildren().add(filteredChild);
        }

    }
}

If you'd like to do it the performing / memory efficient way, you could take a look at the TreeItem example code which will scan a folder only when you navigate into it.



来源:https://stackoverflow.com/questions/34534775/configuring-a-treeview-which-scans-local-fie-system-to-only-include-folders-whic

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!