javafx:如何在 TreeView 中隐藏 "drop down arrow"?
javafx: How do I hide the "drop down arrow" in a TreeView?
希望大家一切都好。
我的问题很简单:如果可能的话,如何隐藏 TreeView 根部的箭头?在 Windows 事件查看器中可以看到我想要实现的一个很好的例子。
虽然我的 javafx 应用程序如下图所示
默认情况下,车辆旁边的箭头存在。
我阅读了文档 here,但我只找到了 .showRoot(),它没有达到我想要的效果。我真的很感激任何输入,即使这是一种作弊的方式(使用 x-offset 属性 在我脑海中闪过)。
箭头是 TreeCell
的 disclosureNode
的一部分。如果未指定,TreeCell
的皮肤有责任提供默认的公开节点(例如三角形)。 属性-setter 文档说明了这一点:
The node to use as the "disclosure" triangle, or toggle, used for expanding and collapsing items. This is only used in the case of an item in the tree which contains child items. If not specified, the TreeCell's Skin implementation is responsible for providing a default disclosure node.
但查看 TreeCellSkin
似乎 没有 提供默认公开节点。相反,这似乎由 TreeViewSkin
处理(在 JavaFX 8 和 JavaFX 11 中)。查看实现,默认公开节点是一个 StackPane
和一个子节点 StackPane
作为实际箭头。他们的风格 class 分别是 tree-disclosure-node
和 arrow
。请注意,这不会出现在任何地方,包括 JavaFX CSS Reference Guide.
在我看来,隐藏根公开节点的最简单且最不容易出错的方法是使用 CSS。这里唯一的问题是 TreeCell
无法提供仅针对根单元格的方法。但这仍然可以通过 subclassing TreeCell
并在我们的 TreeView
.
上提供我们自己的 PseudoClass
. Then we set the cellFactory
来实现
要设置根的图形,请设置根的 TreeItem.graphic
属性 TreeItem
。
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";
}
一些注意事项:
- 因为我在我的单元实现中将
TreeCell.graphic
属性 绑定到 TreeItem.graphic
属性 你将无法设置图形来自 CSS。您可以修改它以简单地设置图形,而不是绑定,以启用该功能。然后你不必通过代码设置根 TreeItem
的图形,但可以做 .custom-tree-cell:root { -fx-graphic: ...; }
.
- 这并没有删除公开节点,只是让它没有宽度和高度。
- 此解决方案依赖于实现细节;更改 JavaFX 版本时要小心。我只在 JavaFX 11.0.2 上尝试过,但我相信它也适用于 JavaFX 8。
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());
}
}
}
main.java
treeView.setCellFactory(param -> {
CustomTreeCell<String> treeCell = new CustomTreeCell<>();
treeCell.setSkin(new MyTreeCellSkin<>(treeCell));
return treeCell;
});
希望大家一切都好。
我的问题很简单:如果可能的话,如何隐藏 TreeView 根部的箭头?在 Windows 事件查看器中可以看到我想要实现的一个很好的例子。
虽然我的 javafx 应用程序如下图所示
默认情况下,车辆旁边的箭头存在。
我阅读了文档 here,但我只找到了 .showRoot(),它没有达到我想要的效果。我真的很感激任何输入,即使这是一种作弊的方式(使用 x-offset 属性 在我脑海中闪过)。
箭头是 TreeCell
的 disclosureNode
的一部分。如果未指定,TreeCell
的皮肤有责任提供默认的公开节点(例如三角形)。 属性-setter 文档说明了这一点:
The node to use as the "disclosure" triangle, or toggle, used for expanding and collapsing items. This is only used in the case of an item in the tree which contains child items. If not specified, the TreeCell's Skin implementation is responsible for providing a default disclosure node.
但查看 TreeCellSkin
似乎 没有 提供默认公开节点。相反,这似乎由 TreeViewSkin
处理(在 JavaFX 8 和 JavaFX 11 中)。查看实现,默认公开节点是一个 StackPane
和一个子节点 StackPane
作为实际箭头。他们的风格 class 分别是 tree-disclosure-node
和 arrow
。请注意,这不会出现在任何地方,包括 JavaFX CSS Reference Guide.
在我看来,隐藏根公开节点的最简单且最不容易出错的方法是使用 CSS。这里唯一的问题是 TreeCell
无法提供仅针对根单元格的方法。但这仍然可以通过 subclassing TreeCell
并在我们的 TreeView
.
PseudoClass
. Then we set the cellFactory
来实现
要设置根的图形,请设置根的 TreeItem.graphic
属性 TreeItem
。
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";
}
一些注意事项:
- 因为我在我的单元实现中将
TreeCell.graphic
属性 绑定到TreeItem.graphic
属性 你将无法设置图形来自 CSS。您可以修改它以简单地设置图形,而不是绑定,以启用该功能。然后你不必通过代码设置根TreeItem
的图形,但可以做.custom-tree-cell:root { -fx-graphic: ...; }
. - 这并没有删除公开节点,只是让它没有宽度和高度。
- 此解决方案依赖于实现细节;更改 JavaFX 版本时要小心。我只在 JavaFX 11.0.2 上尝试过,但我相信它也适用于 JavaFX 8。
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());
}
}
}
main.java
treeView.setCellFactory(param -> {
CustomTreeCell<String> treeCell = new CustomTreeCell<>();
treeCell.setSkin(new MyTreeCellSkin<>(treeCell));
return treeCell;
});