javafx:如何在TreeView中隐藏“下拉箭头”?

时间:2019-03-01 22:56:56

标签: java javafx tree treeview scenebuilder

希望大家都过得很好。

我的问题很基本:如果可能,如何隐藏TreeView根部的箭头?在Windows Event Viewer中可以看到我想要实现的一个很好的例子。

enter image description here

我的javafx应用程序如下图所示

enter image description here

默认情况下,车辆旁边的箭头存在。

我阅读了文档here,但只找到了.showRoot(),它无法实现我想要的功能。我真的很感激任何输入,即使这是一种作弊的方式(使用x-offset属性也让我望而却步。)

2 个答案:

答案 0 :(得分:3)

箭头是TreeCell的{​​{3}}的一部分。未指定时,TreeCell的皮肤有责任提供默认的公开节点(例如三角形)。这由财产设定者文档说明:

  

用作“公开”三角形或切换的节点,用于展开和折叠项目。仅在树中包含子项的项时使用。如果未指定,则TreeCell的Skin实现负责提供默认的公开节点。

但看着TreeCellSkin似乎没有提供默认的公开节点。相反,这似乎由TreeViewSkin处理(在JavaFX 8和JavaFX 11中)。看一下实现,默认的公开节点是一个StackPane,其子StackPane用作实际的箭头。他们的风格类别分别是tree-disclosure-nodearrow。请注意,此文件并未出现在任何地方,包括disclosureNode

在我看来,隐藏根公开节点的最简单,最不易出错的方法是使用CSS。这里唯一的问题是TreeCell无法仅将根单元作为目标。但这仍然可以通过子类TreeCell并提供我们自己的JavaFX CSS Reference Guide来实现。然后,在TreeView上设置PseudoClass

要设置根的图形,请设置根TreeItem的{​​{3}}属性。


CustomTreeCell

import javafx.beans.InvalidationListener;
import javafx.css.PseudoClass;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.util.Callback;

public class CustomTreeCell<T> extends TreeCell<T> {

    private static final PseudoClass ROOT = PseudoClass.getPseudoClass("root");

    public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView() {
        return treeView -> new CustomTreeCell<>();
    }

    public CustomTreeCell() {
        getStyleClass().add("custom-tree-cell");

        InvalidationListener listener = observable -> {
            boolean isRoot = getTreeView() != null && getTreeItem() == getTreeView().getRoot();
            pseudoClassStateChanged(ROOT, isRoot);
        };

        treeViewProperty().addListener(listener);
        treeItemProperty().addListener(listener);
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            graphicProperty().unbind();
            setGraphic(null);
        } else {
            setText(item.toString()); // Really only works if item is a String. Change as needed.
            graphicProperty().bind(getTreeItem().graphicProperty());
        }
    }

}

CSS文件

.custom-tree-cell:root .tree-disclosure-node,
.custom-tree-cell:root .arrow {

    -fx-min-width: 0;
    -fx-pref-width: 0;
    -fx-max-width: 0;

    -fx-min-height: 0;
    -fx-pref-height: 0;
    -fx-max-height: 0;

}

/* Related to question asked in the comments by OP */
.custom-tree-cell > .tree-disclosure-node > .arrow {
    -fx-shape: "M 0 0 L 10 5 L 0 10 L 0 8 L 8 5 L 0 2 Z";
}

一些注意事项:

  1. 自从在单元实现中将TreeCell.graphic属性绑定到TreeItem.graphic属性以来,您将无法从CSS设置图形。您可以修改此设置以仅设置图形而不是绑定,以启用该功能。这样,您就不必通过代码设置根TreeItem的图形,而可以执行.custom-tree-cell:root { -fx-graphic: ...; }
  2. 这不会删除显示节点,只是使其没有宽度和高度。
  3. 此解决方案依赖于实现细节;更改JavaFX版本时要小心。我只在JavaFX 11.0.2上尝试过此方法,但我相信它也应同样适用于JavaFX 8。

答案 1 :(得分:1)

enter image description here

MyTreeCellSkin.java

import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.scene.control.skin.TreeCellSkin;

public class MyTreeCellSkin<T> extends TreeCellSkin<T> {

    public MyTreeCellSkin(TreeCell<T> control) {
        super(control);
    }

    @Override
    protected void layoutChildren(double x, double y, double w, double h) {
        super.layoutChildren(x, y, w, h);

        TreeView<T> tree = getSkinnable().getTreeView();

        int level = tree.getTreeItemLevel(getSkinnable().getTreeItem());
        if (!tree.isShowRoot()) {
            level--;
        }
        double leftMargin = getIndent() * level;

        x += leftMargin;

        double disclosureWidth = 18;

        final int padding = 3;
        // x += disclosureWidth + padding;
        x += 3;

        w -= (leftMargin + disclosureWidth + padding);

        layoutLabelInArea(x, y, w, h);
    }
}

CustomTreeCell.java

import javafx.beans.InvalidationListener;
import javafx.css.PseudoClass;
import javafx.scene.control.TreeCell;

public class CustomTreeCell<T> extends TreeCell<T> {

    private static final PseudoClass ROOT = PseudoClass.getPseudoClass("root");

    public CustomTreeCell() {
        getStyleClass().add("custom-tree-cell");

        InvalidationListener listener = observable -> {
            boolean isRoot = getTreeView() != null && getTreeItem() == getTreeView().getRoot();
            pseudoClassStateChanged(ROOT, isRoot);
        };

        treeViewProperty().addListener(listener);
        treeItemProperty().addListener(listener);
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            graphicProperty().unbind();
            setGraphic(null);
        } else {
            setText(item.toString());
            graphicProperty().bind(getTreeItem().graphicProperty());
        }
    }

}

ma​​in.java

treeView.setCellFactory(param -> {
           CustomTreeCell<String> treeCell = new CustomTreeCell<>();
           treeCell.setSkin(new MyTreeCellSkin<>(treeCell));
           return treeCell;
});