不同 class 字段的 JavaFX TreeItem css 样式
JavaFX TreeItem css style for different class field
我用自己的树填充了 TreeView。在 class 节点中,我有一个字段 "type",它是 NodeType 之一。问题是我想为每种类型的 NodeType 设置样式,例如"type1" 文字颜色应为绿色,"type2" 文字颜色应为红色。我是 javaFX 的新手。我找到了 james-d ( https://github.com/james-d/heterogeneous-tree-example ) 的解决方案,但在这个例子中 css 样式取决于 class 名称,我如何为 class 字段制作它?
View of TreeView
我的理解是,您想要一个 TreeCell
,其样式取决于 TreeCell
的 TreeItem
中包含的 Node
的 NodeType
。全部来自 CSS。我说得对吗?
假设我是正确的,我可以想到两种方法来实现这一点;如果已知的 NodeType
数量较少,这两种方法效果最好。第一个涉及 PseudoClass 的使用,第二个使用与 JavaFX Chart
API.
相同的策略
第一个选项
创建自定义 TreeCell
以使用您的 Node
类型(即适当指定通用签名)。在此自定义 TreeCell
中,您可以根据需要声明尽可能多的 PseudoClass
static final 字段;每个NodeType
一个。然后你观察 TreeCell
当前显示的任何 Node
的 NodeType
并相应地更新 PseudoClass
状态。
这是一个示例,假设 NodeType
是一个 enum
,它有两个常量:HAPPY
和 SAD
。
public class CustomTreeCell<T extends Node> extends TreeCell<T> {
private static final PseudoClass HAPPY = PseudoClass.getPseudoClass("happy");
private static final PseudoClass SAD = PseudoClass.getPseudoClass("sad");
// this listener will activate/deactivate the appropriate PseudoClass states
private final ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
pseudoClassStateChanged(HAPPY, newVal == NodeType.HAPPY);
pseudoClassStateChanged(SAD, newVal == NodeType.SAD);
};
// use a weak listener to avoid a memory leak
private final WeakChangeListener<NodeType> weakListener = /* wrap listener */;
public CustomTreeCell() {
getStyleClass().add("custom-tree-cell");
itemProperty().addListener((obs, oldVal, newVal) -> {
if (oldVal != null) {
oldVal.nodeTypeProperty().removeListener(weakListener);
}
if (newVal != null) {
newVal.nodeTypeProperty().addListener(weakListener);
// need to "observe" the initial NodeType of the new Node item.
// You could call the listener manually to avoid code duplication
pseudoClassStateChanged(HAPPY, newVal.getNodeType() == NodeType.HAPPY);
pseudoClassStateChanged(SAD, newVal.getNodeType() == NodeType.SAD);
} else {
// no item in this cell so deactivate all PseudoClass's
pseudoClassStateChanged(HAPPY, false);
pseudoClassStateChanged(SAD, false);
}
});
}
}
然后在您的 CSS 文件中您可以使用:
.custom-tree-cell:happy {
/* style when happy */
}
.custom-tree-cell:sad {
/* style when sad */
}
第二个选项
做 JavaFX Chart
API 在处理多系列数据时做的事情。它所做的是根据列表中系列的索引动态更新节点的 style class
(例如 .line-chart-series-data-<index>
<-- 可能不是 确切地 this)。
/*
* Create a custom TreeCell like in the first option but
* without any of the PseudoClass code. This listener should
* be added/removed from the Node item just like weakListener
* is above.
*/
ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
// You have to make sure you keep "cell", "indexed-cell", and "tree-cell"
// in order to keep the basic modena styling.
if (newVal == NodeType.HAPPY) {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-happy");
} else if (newVal == NodeType.HAPPY) {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-sad");
} else {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell"); // revert to regular TreeCell style
}
};
然后在CSS:
.custom-tree-cell-happy {
/* styles */
}
.custom-tree-cell-sad {
/* styles */
}
这两个选项实际上仅在存在一小组已知类型时才可行。当你有 10+ NodeType
之类的东西时,它可能变得无法维护。如果 NodeType
的数量在运行时是动态的,这几乎是不可能的。
使用 NodeType
或一些中间 class/data 结构可能更容易,知道文本应该是什么颜色并根据 NodeType
以编程方式设置颜色。
注意:我把答案中的代码快速打出来了,没有测试。我的代码中可能存在编译器错误、运行时异常或逻辑错误。
编辑
我想到了其他事情。我上面的代码假定 NodeType
保存在 属性 中并且可以在运行时更改。如果 NodeType
对于每个 Node
都是静态的(不变的),那么代码可以大大简化。您可以简单地重写 javafx.scene.control.Cell
:
中声明的以下方法,而不是使用任何侦听器
protected void updateItem(Node item, boolean empty)
每次在单元格上设置新项目时都会调用此方法。但是,请阅读 documentation,因为重写此方法需要开发人员进行某些操作(例如调用超级实现)。
我用自己的树填充了 TreeView。在 class 节点中,我有一个字段 "type",它是 NodeType 之一。问题是我想为每种类型的 NodeType 设置样式,例如"type1" 文字颜色应为绿色,"type2" 文字颜色应为红色。我是 javaFX 的新手。我找到了 james-d ( https://github.com/james-d/heterogeneous-tree-example ) 的解决方案,但在这个例子中 css 样式取决于 class 名称,我如何为 class 字段制作它?
View of TreeView
我的理解是,您想要一个 TreeCell
,其样式取决于 TreeCell
的 TreeItem
中包含的 Node
的 NodeType
。全部来自 CSS。我说得对吗?
假设我是正确的,我可以想到两种方法来实现这一点;如果已知的 NodeType
数量较少,这两种方法效果最好。第一个涉及 PseudoClass 的使用,第二个使用与 JavaFX Chart
API.
第一个选项
创建自定义 TreeCell
以使用您的 Node
类型(即适当指定通用签名)。在此自定义 TreeCell
中,您可以根据需要声明尽可能多的 PseudoClass
static final 字段;每个NodeType
一个。然后你观察 TreeCell
当前显示的任何 Node
的 NodeType
并相应地更新 PseudoClass
状态。
这是一个示例,假设 NodeType
是一个 enum
,它有两个常量:HAPPY
和 SAD
。
public class CustomTreeCell<T extends Node> extends TreeCell<T> {
private static final PseudoClass HAPPY = PseudoClass.getPseudoClass("happy");
private static final PseudoClass SAD = PseudoClass.getPseudoClass("sad");
// this listener will activate/deactivate the appropriate PseudoClass states
private final ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
pseudoClassStateChanged(HAPPY, newVal == NodeType.HAPPY);
pseudoClassStateChanged(SAD, newVal == NodeType.SAD);
};
// use a weak listener to avoid a memory leak
private final WeakChangeListener<NodeType> weakListener = /* wrap listener */;
public CustomTreeCell() {
getStyleClass().add("custom-tree-cell");
itemProperty().addListener((obs, oldVal, newVal) -> {
if (oldVal != null) {
oldVal.nodeTypeProperty().removeListener(weakListener);
}
if (newVal != null) {
newVal.nodeTypeProperty().addListener(weakListener);
// need to "observe" the initial NodeType of the new Node item.
// You could call the listener manually to avoid code duplication
pseudoClassStateChanged(HAPPY, newVal.getNodeType() == NodeType.HAPPY);
pseudoClassStateChanged(SAD, newVal.getNodeType() == NodeType.SAD);
} else {
// no item in this cell so deactivate all PseudoClass's
pseudoClassStateChanged(HAPPY, false);
pseudoClassStateChanged(SAD, false);
}
});
}
}
然后在您的 CSS 文件中您可以使用:
.custom-tree-cell:happy {
/* style when happy */
}
.custom-tree-cell:sad {
/* style when sad */
}
第二个选项
做 JavaFX Chart
API 在处理多系列数据时做的事情。它所做的是根据列表中系列的索引动态更新节点的 style class
(例如 .line-chart-series-data-<index>
<-- 可能不是 确切地 this)。
/*
* Create a custom TreeCell like in the first option but
* without any of the PseudoClass code. This listener should
* be added/removed from the Node item just like weakListener
* is above.
*/
ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
// You have to make sure you keep "cell", "indexed-cell", and "tree-cell"
// in order to keep the basic modena styling.
if (newVal == NodeType.HAPPY) {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-happy");
} else if (newVal == NodeType.HAPPY) {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-sad");
} else {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell"); // revert to regular TreeCell style
}
};
然后在CSS:
.custom-tree-cell-happy {
/* styles */
}
.custom-tree-cell-sad {
/* styles */
}
这两个选项实际上仅在存在一小组已知类型时才可行。当你有 10+ NodeType
之类的东西时,它可能变得无法维护。如果 NodeType
的数量在运行时是动态的,这几乎是不可能的。
使用 NodeType
或一些中间 class/data 结构可能更容易,知道文本应该是什么颜色并根据 NodeType
以编程方式设置颜色。
注意:我把答案中的代码快速打出来了,没有测试。我的代码中可能存在编译器错误、运行时异常或逻辑错误。
编辑
我想到了其他事情。我上面的代码假定 NodeType
保存在 属性 中并且可以在运行时更改。如果 NodeType
对于每个 Node
都是静态的(不变的),那么代码可以大大简化。您可以简单地重写 javafx.scene.control.Cell
:
protected void updateItem(Node item, boolean empty)
每次在单元格上设置新项目时都会调用此方法。但是,请阅读 documentation,因为重写此方法需要开发人员进行某些操作(例如调用超级实现)。