JavaFx CheckBoxTreeItem< Path>选择根项目错误 [英] JavaFx CheckBoxTreeItem<Path> selecting root item error

查看:376
本文介绍了JavaFx CheckBoxTreeItem< Path>选择根项目错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试开发文件复制应用程序。我已经在文件系统上创建了一个包含当前目录的复选框树项目。

I'm trying to develop a file copy application. I've created a checkbox tree item with current directories on file system.

但是当我选择第一个节点(c:/ directory)时,需要很长时间。如何轻松快速地选择所有目录?

But when I select first node (c:/ directory), it takes a long time. How can I select all directories easily and quickly?

这是我的第一个FXML加载类:

Here is my first FXML load class:

@Override
public void initialize(URL location, ResourceBundle resources) {

    TreeView pathTree = new MyFileTreeView().getMyFilePathTree();
    vBoxFileTree.getChildren().add(pathTree);
}

这是我的treeView组件:

This is my treeView component:

public class MyFileTreeView {

    private TreeView<Path> filePathTree;
    private List<Path> rootDirectories;
    private Logger logger = Logger.getLogger(MyFileTreeView.class);

    public MyFileTreeView() {

        rootDirectories = new ArrayList<>();

        Iterable<Path> roots = FileSystems.getDefault().getRootDirectories();
        for (Path root : roots) {
            rootDirectories.add(root);
        }
    }

    public TreeView getMyFilePathTree() {

        if (filePathTree == null) {

            filePathTree = new TreeView<>(getRootItem());
            filePathTree.setPrefHeight(600.0d);
            filePathTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
            filePathTree.setCellFactory((TreeView<Path> t) -> new TreeCellImpl());
            filePathTree.setShowRoot(false);
        }

        return filePathTree;
    }

    private TreeItem getRootItem() {

        TreeItem rootItem = new TreeItem();

        for (Path path : rootDirectories) {
            MyFileTreeItem item = new MyFileTreeItem(path);
            item.setIndependent(false);
            rootItem.getChildren().add(item);
            logger.info(path.toString() + " directory has been added to fileTree!");
        }

        return rootItem;
    }
}

这是树项目:

public class MyFileTreeItem extends CheckBoxTreeItem<Path> {

    private boolean isLeaf;
    private boolean isFirstTimeChildren = true;
    private boolean isFirstTimeLeaf = true;

    public MyFileTreeItem(Path path) {
        super(path);
    }

    @Override
    public boolean isLeaf() {

        if (isFirstTimeLeaf) {

            isFirstTimeLeaf = false;
            Path path = getValue();
            isLeaf = Files.isRegularFile(path);
        }
        return isLeaf;
    }

    @Override
    public ObservableList<TreeItem<Path>> getChildren() {

        if (isFirstTimeChildren) {
            isFirstTimeChildren = false;
            super.getChildren().setAll(buildChildren(this));
        }

        return super.getChildren();
    }

    private ObservableList<TreeItem<Path>> buildChildren(CheckBoxTreeItem<Path> treeItem) {

        Path path = treeItem.getValue();
        if ((path != null) && (Files.isDirectory(path))) {

            try (Stream<Path> pathStream = Files.list(path)) {
                return pathStream
                        .map(p -> new MyFileTreeItem(p))
                        .collect(Collectors.toCollection(() ->
                                FXCollections.observableArrayList()));
            } catch (IOException e) {
            }
        }

        return FXCollections.emptyObservableList();
    }
}

更新:

由于@fabian,我添加了不确定的属性

I added indeterminate property thanks to @fabian

public class FileTreeItem extends TreeItem<Path> {

    private boolean isLeaf;
    private boolean isFirstTimeChildren = true;
    private boolean isFirstTimeLeaf = true;

    private BooleanProperty selected;
    private BooleanProperty indeterminate;

    public FileTreeItem(Path path) {
        this(path, false, false);
    }

    protected FileTreeItem(Path path, boolean selected, boolean indeterminate) {

        super(path);
        this.selected = new SimpleBooleanProperty(selected);
        this.indeterminate = new SimpleBooleanProperty(indeterminate);
        this.selected.addListener((o, oldValue, newValue) -> {

            if (!isLeaf() && !isFirstTimeChildren) {

                if (!isIndeterminate()) {
                    for (TreeItem<Path> ti : getChildren()) {
                        ((FileTreeItem) ti).setSelected(newValue);
                    }
                }

                if (isIndeterminate() && newValue) {
                    setIndeterminate(false);
                    for (TreeItem<Path> ti : getChildren()) {
                        ((FileTreeItem) ti).setSelected(newValue);
                    }
                }
            }

            if (!newValue) {

                if (getParent() instanceof FileTreeItem) {
                    FileTreeItem parent = (FileTreeItem) getParent();
                    parent.setIndeterminate(true);
                    parent.setSelected(false);
                }

            } else {

                if (getParent() instanceof FileTreeItem) {

                    boolean allChildSelected = true;
                    FileTreeItem parent = (FileTreeItem) getParent();
                    for (TreeItem<Path> child : parent.getChildren()) {
                        if (!((FileTreeItem) child).isSelected()) {
                            allChildSelected = false;
                            break;
                        }
                    }

                    if (allChildSelected && !parent.isSelected()) {
                        setIndeterminate(false);
                        parent.setIndeterminate(false);
                        parent.setSelected(true);
                    }
                }
            }
        });
    }

    @Override
    public boolean isLeaf() {

        if (isFirstTimeLeaf) {
            isFirstTimeLeaf = false;
            Path path = getValue();
            isLeaf = Files.isRegularFile(path);
        }
        return isLeaf;
    }

    @Override
    public ObservableList<TreeItem<Path>> getChildren() {

        if (isFirstTimeChildren) {
            isFirstTimeChildren = false;
            super.getChildren().setAll(buildChildren(this));
        }

        return super.getChildren();
    }

    private List<TreeItem<Path>> buildChildren(FileTreeItem treeItem) {

        Path path = treeItem.getValue();
        if ((path != null) && (Files.isDirectory(path))) {

            final boolean select = treeItem.isSelected();
            boolean indeterminate = treeItem.isIndeterminate();

            try (Stream<Path> pathStream = Files.list(path)) {

                List<TreeItem<Path>> res = new ArrayList<>();
                pathStream
                        .map(p -> new FileTreeItem(p, select, indeterminate))
                        .forEach(res::add);
                return res;
            } catch (IOException e) {
            }
        }

        return Collections.emptyList();
    }

    public boolean isSelected() {
        return selected.get();
    }

    public BooleanProperty selectedProperty() {
        return selected;
    }

    public void setSelected(boolean value) {
        selected.set(value);
    }

    public boolean isIndeterminate() {
        return indeterminate.get();
    }

    public BooleanProperty indeterminateProperty() {
        return indeterminate;
    }

    public void setIndeterminate(boolean indeterminate) {
        this.indeterminate.set(indeterminate);
    }
}


推荐答案

您更改 CheckBoxTreeItem的选定状态子项的状态设置为相同的值。这意味着将调用 getChildren 方法,并且对于所有子项,也会设置 selected 属性。这样你就可以有效地对目录的所有内容进行深度优先遍历。

When you change the selected state of a CheckBoxTreeItem the state of child items is set to the same value. This means the getChildren method is called and for all children the selected property is set too. This way you effectively do a depth first traversal of all the contents of a directory.

你需要直接扩展 TreeItem 出于这个原因并实现所需的属性。您需要确保何时更新选定的属性,只有在 getChildren 已经存在的情况下才会遍历子项调用:

You need directly extend TreeItem for this reason and implement the required properties. You need to make sure when the selected property is updated, you iterate through the child items only if getChildren has already been called:

public class FileTreeItem extends TreeItem<Path> {

    private boolean isLeaf;
    private boolean isFirstTimeChildren = true;
    private boolean isFirstTimeLeaf = true;

    private final BooleanProperty selected;
    private final BooleanProperty indeterminate;

    protected FileTreeItem(Path path, boolean selected) {
        super(path);
        this.indeterminate = new SimpleBooleanProperty();
        this.selected = new SimpleBooleanProperty(selected);
        this.selected.addListener((o, oldValue, newValue) -> {
            if (!updating) {
                if (!isLeaf() && !isFirstTimeChildren) {
                    // propagate selection to children if they were created yet
                    for (TreeItem<Path> ti : getChildren()) {
                        FileTreeItem fti = (FileTreeItem) ti;
                        fti.setSelected(newValue);
                    }
                }

                // update ancestors
                TreeItem<Path> parent = getParent();
                while ((parent instanceof FileTreeItem)
                        && updateAncestorState((FileTreeItem) parent)) {
                    parent = parent.getParent();
                }
            }
        });
    }

    /**
     * flag preventing circular calls during update.
     */
    private boolean updating;

    protected static boolean updateAncestorState(FileTreeItem item) {
        List<TreeItem<Path>> children = item.getChildren();

        boolean hasUnselected = false;
        boolean hasSelected = false;
        for (Iterator<TreeItem<Path>> it = children.iterator();!(hasSelected && hasUnselected) && it.hasNext();) {
            TreeItem<Path> ti = it.next();
            FileTreeItem child = (FileTreeItem) ti;
            if (child.isSelected()) {
                hasSelected = true;
            } else {
                hasUnselected = true;
                if (child.isIndeterminate()) {
                    hasSelected = true;
                }
            }
        }

        item.updating = true;
        boolean changed = false;

        if (hasUnselected) {
            if (item.isSelected() || item.isIndeterminate() != hasSelected) {
                changed = true;
                item.setSelected(false);
                item.setIndeterminate(hasSelected);
            }
        } else {
            if (!item.isSelected()) {
                changed = true;
                item.setSelected(true);
            }
            item.setIndeterminate(false);
        }
        item.updating = false;

        return changed;
    }

    public FileTreeItem(Path path) {
        this(path, false);
    }

    @Override
    public boolean isLeaf() {
        if (isFirstTimeLeaf) {
            isFirstTimeLeaf = false;
            Path path = getValue();
            isLeaf = Files.isRegularFile(path);
        }
        return isLeaf;
    }

    @Override
    public ObservableList<TreeItem<Path>> getChildren() {

        if (isFirstTimeChildren) {
            isFirstTimeChildren = false;
            super.getChildren().setAll(buildChildren(this));
        }

        return super.getChildren();
    }

    private List<TreeItem<Path>> buildChildren(FileTreeItem treeItem) {
        Path path = treeItem.getValue();
        if ((path != null) && (Files.isDirectory(path))) {
            final boolean select = treeItem.isSelected();
            try (Stream<Path> pathStream = Files.list(path)) {
                List<TreeItem<Path>> res = new ArrayList<>();
                pathStream
                        .map(p -> new FileTreeItem(p, select))
                        .forEach(res::add);
                return res;
            } catch (IOException e) {
            }
        }

        return Collections.emptyList();
    }

    /* methods for selected & indeterminate properties */
}

编辑

显示 indeterminate 属性需要您实现自己的 TreeCell

Displaying the indeterminate property requires you to implement your own TreeCell:

public class FileItemCheckBoxTreeCell extends TreeCell<Path> {

    private BooleanProperty oldSelectedProperty;
    private BooleanProperty oldIndeterminateProperty;

    private final CheckBox checkBox;
    private final StringConverter<TreeItem<Path>> converter;

    public FileItemCheckBoxTreeCell(StringConverter<TreeItem<Path>> converter) {
        if (converter == null) {
            throw new IllegalArgumentException();
        }
        this.converter = converter;
        this.checkBox = new CheckBox();
    }

    @Override
    protected void updateItem(Path item, boolean empty) {
        // clear old binding
        if (oldSelectedProperty != null) {
            checkBox.selectedProperty().unbindBidirectional(oldSelectedProperty);
            checkBox.indeterminateProperty().unbindBidirectional(oldIndeterminateProperty);
            oldSelectedProperty = null;
            oldIndeterminateProperty = null;
        }
        checkBox.indeterminateProperty().unbind();

        super.updateItem(item, empty);

        if (empty) {
            setGraphic(null);
            setText("");
        } else {
            TreeItem<Path> treeItem = getTreeItem();
            setText(converter.toString(treeItem));
            if (treeItem instanceof FileTreeItem) {
                setGraphic(checkBox);
                FileTreeItem fti = (FileTreeItem) treeItem;

                oldSelectedProperty = fti.selectedProperty();
                oldIndeterminateProperty = fti.indeterminateProperty();

                checkBox.selectedProperty().bindBidirectional(oldSelectedProperty);
                checkBox.indeterminateProperty().bindBidirectional(oldIndeterminateProperty);
            } else {
                setGraphic(null);
            }
        }
    }

    public static Callback<TreeView<Path>, TreeCell<Path>> forTreeView(StringConverter<TreeItem<Path>> converter) {
        if (converter == null) {
            throw new IllegalArgumentException();
        }
        return tv -> new FileItemCheckBoxTreeCell(converter);
    }

}





public TreeView getMyFilePathTree() {

    if (filePathTree == null) {

        filePathTree = new TreeView<>(getRootItem());
        filePathTree.setPrefHeight(600.0d);
         filePathTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

        // tell cell factory to use the selected property for checkbox
        filePathTree.setCellFactory(FileItemCheckBoxTreeCell.forTreeView(new StringConverter<TreeItem<Path>>() {

            @Override
            public String toString(TreeItem<Path> object) {
                if (object == null) {
                    return "";
                }
                Path p = object.getValue();
                if (p == null) {
                    return "";
                }
                p = p.getFileName();
                return p == null ? object.getValue().toString() : p.toString();
            }

            @Override
            public TreeItem<Path> fromString(String string) {
                throw new UnsupportedOperationException();
            }

        }));
        filePathTree.setShowRoot(false);
    }

    return filePathTree;
}

这篇关于JavaFx CheckBoxTreeItem&lt; Path&gt;选择根项目错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆