Настройка TreeView, который сканирует локальную файловую систему, чтобы включать только папки, имеющие тип файла

Итак, я использовал второй блок кода с этого веб-сайта http://www.java2s.com/Tutorials/Java/JavaFX/0660__JavaFX_Tree_View.htm, где указано: «Следующий код создает динамическое дерево из локальной файловой системы».

Я не понимаю, как работает этот код, чтобы настроить его под свои нужды. В частности, методы переопределения, похоже, не было места, где я мог бы добавить «только добавлять папки в подкаталог, содержащий файлы mp3». Я считаю, что, вероятно, потребуется что-то более сложное, например, что-то, что проходит и удаляет папки. Я честно не уверен.

Я попытался использовать этот код в своей программе, которая должна показывать файлы mp3. Идея заключалась в том, чтобы иметь два treeView рядом друг с другом, левая сторона показывает иерархию папок с папками, в которых есть mp3-файлы (и не показывает другие папки, в которых нет mp3-файлов), а правая сторона показывает файлы которые имеют только тип файла mp3 в этих папках. Ниже есть скриншот.

Это код, который у меня есть до сих пор, который возвращает TreeView в VBox. Есть два сегмента кода, которые закомментированы. Во-первых, это связано с тем, что java: поиск файла по его имени в каталоге и подкаталогах не хочет выполнять поиск на моем диске C:. (Я не знаю почему). Поэтому я изменил его, чтобы сканировать только мой D: (раздел диска). Второй — с веб-страницы, где я получил основной сегмент кода. Этот код был перемещен во внешний класс, который обрабатывает. А также дерзкий код для работы с более чем одним диском.

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

Предварительный просмотр части графического интерфейса, TreeView слева относится к классу, показанному выше


person Gideon Sassoon    schedule 30.12.2015    source источник
comment
Взгляните на руководство Oracle по поиску файлов. Будьте осторожны, пытаясь использовать случайный код, который вы найдете в Интернете и не сможете понять.   -  person jewelsea    schedule 30.12.2015


Ответы (1)


Вы можете создать древовидную структуру рекурсивно. Существуют различные механизмы (пожалуйста, обратите внимание на комментарий к вашему вопросу от Jewelsea), вот один из них:

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);
    }
}

Тогда у вас есть дерево со всеми вашими файлами. Поскольку вы собираетесь сканировать всю древовидную структуру по заданному корневому пути и хранить ее в памяти, вы также можете просто отфильтровать дерево. Я взял приведенный выше код и код фильтра из ответа rli в этом сообщении и адаптировал его. Он в основном создает отфильтрованную древовидную структуру из исходной структуры.

Вот полный пример кода:

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);
    }
}

Просто введите текст поиска в текстовое поле, дерево будет соответствующим образом отфильтровано.

Вы можете изменить метод toString(), если хотите увидеть полный путь. Кроме того, в настоящее время ищется подстрока вместо расширения файла. Это только для демо, адаптировать под свои нужды тривиально.

Аналогичный механизм применяется, если вы хотите отобразить части дерева в другом представлении в виде дерева или списка.

Пример, если вы хотите получить древовидную структуру со всеми узлами, которые содержат файл с e. грамм. подстрока "mp3" в текстовом фильтре, при этом не отображая сам файл в дереве, вот модифицированная версия метода фильтра:

/**
 * 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);
        }

    }
}

Если вы хотите сделать это с эффективным использованием производительности/памяти, вы можете взглянуть на TreeItem пример кода, который будет сканировать папку только при переходе в нее.

person Roland    schedule 31.12.2015
comment
Спасибо за вашу помощь, однако реализация и запуск вашего кода привели к следующему исключению (я понимаю, что первые шаги - это поиск в Google, но недопустимым аргументом может быть практически что угодно. Caused by: java.lang.IllegalArgumentException at FileTreeViewTwo$FilePath.<init>(FileTreeViewTwo.java:167) at FileTreeViewTwo.createTreeRoot(FileTreeViewTwo.java:151) at FileTreeViewTwo.createTree(FileTreeViewTwo.java:60) at FileTreeViewTwo.start(FileTreeViewTwo.java:47) Строка 167: this.text = path.getName( path.getNameCount() - 1).toString(); Не совсем уверен, что в этом незаконного. - person Gideon Sassoon; 03.01.2016
comment
Звучит как ошибка, которую легко решить. Используйте путь System.out.println(path); до того, как этот метод будет вызван, и опубликуйте вывод прямо перед исключением. Эта кодовая строка просто использует последнюю часть пути, например. грамм. для c:\music\mine у ​​вас будет мой как текст узла в дереве вместо полного пути. И что вы установили для ROOT_FOLDER? - person Roland; 03.01.2016
comment
Корневая папка установлена ​​​​в C:\\ вызов команды в println вызывает исключение - person Gideon Sassoon; 04.01.2016
comment
Исправлено, было тривиальное исключение индекса. Я обновил полный код примера. После этого было также исключение AccessDeniedException для корзины, поэтому я добавил предложение catch в предложение try-with-resources. Вы можете адаптировать его к вашим потребностям. - person Roland; 04.01.2016