天天看点

JavaFX踩坑日记3_使用TreeTableView高效地列出文件夹树

首先我们先来构建UI代码。首先是文件条目的bean PathItem,这个树状图我打算显示文件条目的同时显示文件的大小:

package com.bluepoint.bean;

import javafx.beans.property.SimpleStringProperty;

import java.io.File;

public class PathItem {
    private SimpleStringProperty mDirName;
    private SimpleStringProperty mDirSize;
    private File mFile;

    public PathItem(String path) {
        mFile = new File(path);
        mDirName = new SimpleStringProperty(mFile.getName());
        if (mFile.isDirectory()) {
            mDirSize = new SimpleStringProperty(""); //todo 还没想到不用遍历的方式获取文件夹大小的方法,遍历太深的话时间要爆炸
        } else {
            mDirSize = new SimpleStringProperty("" + mFile.length());
        }
    }

    public String getDirName() {
        return mDirName.get();
    }

    public void setDirName(String dirName) {
        this.mDirName.set(dirName);
    }

    public String getDirSize() {
        return mDirSize.get();
    }

    public void setDirSize(String dirSize) {
        this.mDirSize.set(dirSize);
    }

    public File getFile() {
        return mFile;
    }
}
           

然后是界面的逻辑:

树状图的呈现我使用了TreeTableView,具体的view和列和bean的关联代码如下:

其中TreeTableView<PathItem> dirsTree是我在xml中已经创建好的TreeTableView对象,因此不是用new的方式获取该对象。

TreeTableView<PathItem> dirsTree = (TreeTableView) localPageLeftMenu.lookup("#ttv_dirs");

        TreeTableColumn<PathItem, String> dirsColumn = new TreeTableColumn<>(
                "名称");
        dirsColumn
                .setCellValueFactory((
                        TreeTableColumn.CellDataFeatures<PathItem, String> param) -> new ReadOnlyStringWrapper(
                        param.getValue().getValue().getDirName()));

        TreeTableColumn<PathItem, String> sizeColumn = new TreeTableColumn<>(
                "大小");
        sizeColumn
                .setCellValueFactory((
                        TreeTableColumn.CellDataFeatures<PathItem, String> param) -> new ReadOnlyStringWrapper(
                        param.getValue().getValue().getDirSize()));
        dirsTree.getColumns().clear();
        dirsColumn.setPrefWidth(dirsTree.getPrefWidth() / 2);
        sizeColumn.setPrefWidth(dirsTree.getPrefWidth() / 2);
        dirsTree.getColumns().add(dirsColumn);
        dirsTree.getColumns().add(sizeColumn);
           

最后就是重头戏了,如何高效地呈现界面树的逻辑:

1、首先设置根结点。

2、给TreeTableView设置条目点击事件,点击的时候返回其对应的bean对象的file对象,假如其是一个文件夹,则往当前点击回调的结点中遍历生成对应的叶子结点,为了避免每次点击都追加了一次一样的叶子,因此每次需要把上次可能存在的叶子结点都清理一次(也不能说生成过一次,点击就不再生成了,万一用户新建了文件和文件夹,就会导致无法通过点击刷新的问题)。同时,如果当前添加的叶子文件是飞空文件夹,则添加一个空数据作为叶子的叶子,使得用户看到这个文件夹时知道可以继续点击:

为什么不一口气遍历整个文件数,然后一边遍历一边生成所有结点呢?首先这样性能会非常差,二来是不必要,用户只是想点击哪个就展开哪个的下一层,因此我认为,我这样的做法是最合理的。

TreeItem<PathItem> root = new TreeItem<>(new PathItem("/")); //todo 暂时设为根目录
        //应用数据集:
        dirsTree.setRoot(root);
        //为文件夹树设置点击事件:
        dirsTree.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
            public void handle(MouseEvent event) {
                TreeItem node = (TreeItem) dirsTree.getSelectionModel().getSelectedItem();
                PathItem item = (PathItem) node.getValue();
                System.out.println("Node click: " + item.getDirName());
                if (item.getFile().isDirectory()) {
                    node.getChildren().clear();
                    File filesList[] = item.getFile().listFiles();
                    if (filesList == null || filesList.length == 0) {
                        System.out.println("this is an empty directory.");
                    } else {
                        for (File s : filesList) {
                            PathItem nextDirUnit = new PathItem(s.getAbsolutePath());
                            TreeItem nextDirItem = new TreeItem<>(nextDirUnit);
                            if (nextDirUnit.getFile().isDirectory() && nextDirUnit.getFile().list() != null && nextDirUnit.getFile().list().length > 0) { //如果当前添加的叶子文件是飞空文件夹,则添加一个空数据作为叶子的叶子,使得用户看到这个文件夹时知道可以继续点击
                                nextDirItem.getChildren().add(new TreeItem<>());
                            }
                            node.getChildren().add(nextDirItem);
                        }
                    }
                } else {
                    System.out.println("this is not a directory.");
                }
            }
        });
           

最终效果:

JavaFX踩坑日记3_使用TreeTableView高效地列出文件夹树