OpenJFX 16 自定义 TreeTableCell 缩进不正确
OpenJFX 16 custom TreeTableCell not indenting properly
更新 - 在末尾添加了最小的可重现示例
我正在努力将 JavaFX 8 应用程序迁移到 OpenJFX / JDK 16.
除了应用程序有一个带有自定义单元格工厂的 TreeTableView 控件外,一切都运行良好。单元格全部布局为与 TreeTableView 边界左对齐。我的期望是每个都将根据其在树中的级别进行适当的缩进。
这里是细胞工厂调用的自定义 TreeTableCell 的代码。
package com.mycorp.myapp.features.saleshierarchy;
import com.mycorp.myapp.domain.SalesEntity;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TreeTableCell;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.TextAlignment;
public class SalesTreeCell extends TreeTableCell<SalesEntity, SalesEntity> {
private Label codeLabel = new Label();
private Label descriptionLabel = new Label();
private HBox group = new HBox(codeLabel, descriptionLabel);
public SalesTreeCell() {
}
@Override
protected void updateItem(SalesEntity item, boolean empty) {
super.updateItem(item, empty);
if ( empty || item == null ) {
setGraphic(null);
} else {
final Color textColor;
final Color backgroundColor;
textColor = Color.DARKGOLDENROD;
backgroundColor = Color.TRANSPARENT;
codeLabel.setText(item.getDisplayName());
codeLabel.setFont(Font.font("Arial", 12));
codeLabel.setTextFill(textColor);
codeLabel.setBackground(new Background(new BackgroundFill(backgroundColor, CornerRadii.EMPTY, Insets.EMPTY)));
descriptionLabel.setText(item.getDisplayDescription());
descriptionLabel.setFont(Font.font("Arial", FontPosture.ITALIC, 10));
descriptionLabel.setTextFill(canControlNode ? Color.DARKSLATEGRAY : Color.GRAY);
group.setSpacing(10);
setGraphic(group);
}
}
}
如何让 OpenJFX 16 正确对齐我的自定义 TreeTableCell?我尝试了各种对齐方式并尝试查看 Javadocs,但到目前为止没有任何效果。我在想也许我可以用 CSS 风格做点什么?
JDK 是 Amazon Corretto 16,如果重要的话。 (注意:由于外部原因,我们被迫使用版本 16)。
最小可重现示例:
package com.subaru.SAM.sandbox;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.stage.Stage;
public class TestApp extends javafx.application.Application {
// Class for holding tree table data
public static class MyTreeItem {
public String name;
public String description;
public final List<TestApp.MyTreeItem> children = new ArrayList<>();
public MyTreeItem( final String name, final String description, final MyTreeItem parent ) {
this.name = name;
this.description = description;
if ( parent != null ) {
parent.children.add(this); // Bad form to allow this to escape constructor, but this is quick and dirty and we're single threaded anyway.
}
}
@Override
public String toString() {
return this.name + " ... " + this.description;
}
}
// Class for cell factory (to create TreeTableCell objects for TreeTableView)
public static class MyTreeCell extends TreeTableCell<TestApp.MyTreeItem, TestApp.MyTreeItem> {
private Label nameLabel = new Label();
private Label descriptionLabel = new Label();
private HBox group = new HBox(nameLabel, descriptionLabel);
public MyTreeCell() {
}
@Override
protected void updateItem(MyTreeItem item, boolean empty) {
super.updateItem(item, empty);
if ( empty || item == null ) {
setGraphic(null);
} else {
final Color textColor;
final Color backgroundColor;
textColor = Color.DARKGOLDENROD;
backgroundColor = Color.TRANSPARENT;
nameLabel.setText(item.name);
nameLabel.setFont(Font.font("Arial", 12)); // FIXME Make this a preference
nameLabel.setTextFill(textColor);
nameLabel.setBackground(new Background(new BackgroundFill(backgroundColor, CornerRadii.EMPTY, Insets.EMPTY)));
descriptionLabel.setText(item.description);
descriptionLabel.setFont(Font.font("Arial", FontPosture.ITALIC, 10));
group.setSpacing(10);
setGraphic(group);
}
}
}
@Override
public void start(Stage stage) {
// Make a tree table view
final TreeTableView<MyTreeItem> treeTableView = new TreeTableView<>();
// Set data for tree table
final MyTreeItem root = new MyTreeItem("Root Node","This is the root node", null);
final TreeItem<MyTreeItem> rootTreeItem = new TreeItem<>(root);
for ( int i = 0; i < 5; i++) {
final MyTreeItem nodeI = new MyTreeItem("Node " + i,"This is node " + i, root);
final TreeItem<MyTreeItem> treeItemI = new TreeItem<>(nodeI);
rootTreeItem.getChildren().add(treeItemI);
for ( int j = 0; j < 3; j++ ) {
final MyTreeItem nodeJ = new MyTreeItem("Node " + i + "." + j,"This is node " + i + "." + j, nodeI);
final TreeItem<MyTreeItem> treeItemJ = new TreeItem<>(nodeJ);
treeItemI.getChildren().add(treeItemJ);
for ( int k = 0; k < 10; k++ ) {
final MyTreeItem nodeK = new MyTreeItem("Node " + i + "." + j + "." + k,"This is node " + i + "." + j + "." + k, nodeJ);
final TreeItem<MyTreeItem> treeItemK = new TreeItem<>(nodeK);
treeItemJ.getChildren().add(treeItemK);
}
}
}
rootTreeItem.setExpanded(true);
// Create a column
final TreeTableColumn<MyTreeItem, MyTreeItem> ttc = new TreeTableColumn<>();
// ttc.setMinWidth(600);
ttc.setCellValueFactory((CellDataFeatures<MyTreeItem, MyTreeItem> p) -> {
final TreeItem<MyTreeItem> treeItem = p.getValue();
return new ReadOnlyObjectWrapper<MyTreeItem>(treeItem == null ? null : treeItem.getValue());
});
ttc.setCellFactory(param -> new MyTreeCell());
treeTableView.getColumns().add(ttc);
treeTableView.setRoot(rootTreeItem);
StackPane mainPane = new StackPane(treeTableView);
Scene scene = new Scene(new StackPane(mainPane), 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
结果:
如果我注释掉这一行:
ttc.setCellFactory(param -> new MyTreeCell());
...问题没有出现。似乎仅限于使用自定义 TreeTableCell class.
我不确定 Java 8 是如何工作的,但是 API 对于 updateItem()
seems to require invoking setText()
for non-empty cells, even if the text is not visible. As @VGR ,使用零宽度 space:
setText("\u2060");
或者,像往常一样显示 description
并将 name
设置为图形,如下所示。
作为@kleopatra , this is a bug, fixed in fx18. See also JDK-8278904—Cell: misleading doc of updateItem。
经测试:
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/** */
public class TreeTableApp extends javafx.application.Application {
// Class for holding tree table data
public static class MyTreeItem {
public String name;
public String description;
public final List<MyTreeItem> children = new ArrayList<>();
public MyTreeItem(final String name, final String description, final MyTreeItem parent) {
this.name = name;
this.description = description;
if (parent != null) {
parent.children.add(this);
}
}
@Override
public String toString() {
return this.name + "—" + this.description;
}
}
// Class for cell factory (to create TreeTableCell objects for TreeTableView)
private static class MyTreeCell extends TreeTableCell<MyTreeItem, MyTreeItem> {
private final Label nameLabel = new Label();
@Override
protected void updateItem(MyTreeItem item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
nameLabel.setText(item.name);
nameLabel.setTextFill(Color.DARKGOLDENROD);
setGraphicTextGap(8);
setGraphic(nameLabel);
setText(item.description);
setTextFill(Color.BLACK);
}
}
}
@Override
public void start(Stage stage) {
// Make a tree table view
final TreeTableView<MyTreeItem> treeTableView = new TreeTableView<>();
// Set data for tree table
final MyTreeItem root = new MyTreeItem("Root Node", "This is the root node", null);
final TreeItem<MyTreeItem> rootTreeItem = new TreeItem<>(root);
for (int i = 0; i < 5; i++) {
final MyTreeItem nodeI = new MyTreeItem("Node " + i, "This is node " + i, root);
final TreeItem<MyTreeItem> treeItemI = new TreeItem<>(nodeI);
rootTreeItem.getChildren().add(treeItemI);
for (int j = 0; j < 3; j++) {
final MyTreeItem nodeJ = new MyTreeItem("Node " + i + "." + j, "This is node " + i + "." + j, nodeI);
final TreeItem<MyTreeItem> treeItemJ = new TreeItem<>(nodeJ);
treeItemI.getChildren().add(treeItemJ);
for (int k = 0; k < 10; k++) {
final MyTreeItem nodeK = new MyTreeItem("Node " + i + "." + j + "." + k, "This is node " + i + "." + j + "." + k, nodeJ);
final TreeItem<MyTreeItem> treeItemK = new TreeItem<>(nodeK);
treeItemJ.getChildren().add(treeItemK);
}
}
}
rootTreeItem.setExpanded(true);
// Create a column
final TreeTableColumn<MyTreeItem, MyTreeItem> ttc = new TreeTableColumn<>();
ttc.setCellValueFactory((CellDataFeatures<MyTreeItem, MyTreeItem> p) -> {
final TreeItem<MyTreeItem> treeItem = p.getValue();
return new ReadOnlyObjectWrapper<>(treeItem == null ? null : treeItem.getValue());
});
ttc.setCellFactory(param -> new MyTreeCell());
ttc.setPrefWidth(320);
treeTableView.getColumns().add(ttc);
treeTableView.setRoot(rootTreeItem);
StackPane mainPane = new StackPane(treeTableView);
Scene scene = new Scene(new StackPane(mainPane), 320, 240);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
更新 - 在末尾添加了最小的可重现示例
我正在努力将 JavaFX 8 应用程序迁移到 OpenJFX / JDK 16.
除了应用程序有一个带有自定义单元格工厂的 TreeTableView 控件外,一切都运行良好。单元格全部布局为与 TreeTableView 边界左对齐。我的期望是每个都将根据其在树中的级别进行适当的缩进。
这里是细胞工厂调用的自定义 TreeTableCell 的代码。
package com.mycorp.myapp.features.saleshierarchy;
import com.mycorp.myapp.domain.SalesEntity;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TreeTableCell;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.TextAlignment;
public class SalesTreeCell extends TreeTableCell<SalesEntity, SalesEntity> {
private Label codeLabel = new Label();
private Label descriptionLabel = new Label();
private HBox group = new HBox(codeLabel, descriptionLabel);
public SalesTreeCell() {
}
@Override
protected void updateItem(SalesEntity item, boolean empty) {
super.updateItem(item, empty);
if ( empty || item == null ) {
setGraphic(null);
} else {
final Color textColor;
final Color backgroundColor;
textColor = Color.DARKGOLDENROD;
backgroundColor = Color.TRANSPARENT;
codeLabel.setText(item.getDisplayName());
codeLabel.setFont(Font.font("Arial", 12));
codeLabel.setTextFill(textColor);
codeLabel.setBackground(new Background(new BackgroundFill(backgroundColor, CornerRadii.EMPTY, Insets.EMPTY)));
descriptionLabel.setText(item.getDisplayDescription());
descriptionLabel.setFont(Font.font("Arial", FontPosture.ITALIC, 10));
descriptionLabel.setTextFill(canControlNode ? Color.DARKSLATEGRAY : Color.GRAY);
group.setSpacing(10);
setGraphic(group);
}
}
}
如何让 OpenJFX 16 正确对齐我的自定义 TreeTableCell?我尝试了各种对齐方式并尝试查看 Javadocs,但到目前为止没有任何效果。我在想也许我可以用 CSS 风格做点什么?
JDK 是 Amazon Corretto 16,如果重要的话。 (注意:由于外部原因,我们被迫使用版本 16)。
最小可重现示例:
package com.subaru.SAM.sandbox;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.stage.Stage;
public class TestApp extends javafx.application.Application {
// Class for holding tree table data
public static class MyTreeItem {
public String name;
public String description;
public final List<TestApp.MyTreeItem> children = new ArrayList<>();
public MyTreeItem( final String name, final String description, final MyTreeItem parent ) {
this.name = name;
this.description = description;
if ( parent != null ) {
parent.children.add(this); // Bad form to allow this to escape constructor, but this is quick and dirty and we're single threaded anyway.
}
}
@Override
public String toString() {
return this.name + " ... " + this.description;
}
}
// Class for cell factory (to create TreeTableCell objects for TreeTableView)
public static class MyTreeCell extends TreeTableCell<TestApp.MyTreeItem, TestApp.MyTreeItem> {
private Label nameLabel = new Label();
private Label descriptionLabel = new Label();
private HBox group = new HBox(nameLabel, descriptionLabel);
public MyTreeCell() {
}
@Override
protected void updateItem(MyTreeItem item, boolean empty) {
super.updateItem(item, empty);
if ( empty || item == null ) {
setGraphic(null);
} else {
final Color textColor;
final Color backgroundColor;
textColor = Color.DARKGOLDENROD;
backgroundColor = Color.TRANSPARENT;
nameLabel.setText(item.name);
nameLabel.setFont(Font.font("Arial", 12)); // FIXME Make this a preference
nameLabel.setTextFill(textColor);
nameLabel.setBackground(new Background(new BackgroundFill(backgroundColor, CornerRadii.EMPTY, Insets.EMPTY)));
descriptionLabel.setText(item.description);
descriptionLabel.setFont(Font.font("Arial", FontPosture.ITALIC, 10));
group.setSpacing(10);
setGraphic(group);
}
}
}
@Override
public void start(Stage stage) {
// Make a tree table view
final TreeTableView<MyTreeItem> treeTableView = new TreeTableView<>();
// Set data for tree table
final MyTreeItem root = new MyTreeItem("Root Node","This is the root node", null);
final TreeItem<MyTreeItem> rootTreeItem = new TreeItem<>(root);
for ( int i = 0; i < 5; i++) {
final MyTreeItem nodeI = new MyTreeItem("Node " + i,"This is node " + i, root);
final TreeItem<MyTreeItem> treeItemI = new TreeItem<>(nodeI);
rootTreeItem.getChildren().add(treeItemI);
for ( int j = 0; j < 3; j++ ) {
final MyTreeItem nodeJ = new MyTreeItem("Node " + i + "." + j,"This is node " + i + "." + j, nodeI);
final TreeItem<MyTreeItem> treeItemJ = new TreeItem<>(nodeJ);
treeItemI.getChildren().add(treeItemJ);
for ( int k = 0; k < 10; k++ ) {
final MyTreeItem nodeK = new MyTreeItem("Node " + i + "." + j + "." + k,"This is node " + i + "." + j + "." + k, nodeJ);
final TreeItem<MyTreeItem> treeItemK = new TreeItem<>(nodeK);
treeItemJ.getChildren().add(treeItemK);
}
}
}
rootTreeItem.setExpanded(true);
// Create a column
final TreeTableColumn<MyTreeItem, MyTreeItem> ttc = new TreeTableColumn<>();
// ttc.setMinWidth(600);
ttc.setCellValueFactory((CellDataFeatures<MyTreeItem, MyTreeItem> p) -> {
final TreeItem<MyTreeItem> treeItem = p.getValue();
return new ReadOnlyObjectWrapper<MyTreeItem>(treeItem == null ? null : treeItem.getValue());
});
ttc.setCellFactory(param -> new MyTreeCell());
treeTableView.getColumns().add(ttc);
treeTableView.setRoot(rootTreeItem);
StackPane mainPane = new StackPane(treeTableView);
Scene scene = new Scene(new StackPane(mainPane), 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
结果:
如果我注释掉这一行:
ttc.setCellFactory(param -> new MyTreeCell());
...问题没有出现。似乎仅限于使用自定义 TreeTableCell class.
我不确定 Java 8 是如何工作的,但是 API 对于 updateItem()
seems to require invoking setText()
for non-empty cells, even if the text is not visible. As @VGR
setText("\u2060");
或者,像往常一样显示 description
并将 name
设置为图形,如下所示。
作为@kleopatra
经测试:
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/** */
public class TreeTableApp extends javafx.application.Application {
// Class for holding tree table data
public static class MyTreeItem {
public String name;
public String description;
public final List<MyTreeItem> children = new ArrayList<>();
public MyTreeItem(final String name, final String description, final MyTreeItem parent) {
this.name = name;
this.description = description;
if (parent != null) {
parent.children.add(this);
}
}
@Override
public String toString() {
return this.name + "—" + this.description;
}
}
// Class for cell factory (to create TreeTableCell objects for TreeTableView)
private static class MyTreeCell extends TreeTableCell<MyTreeItem, MyTreeItem> {
private final Label nameLabel = new Label();
@Override
protected void updateItem(MyTreeItem item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
nameLabel.setText(item.name);
nameLabel.setTextFill(Color.DARKGOLDENROD);
setGraphicTextGap(8);
setGraphic(nameLabel);
setText(item.description);
setTextFill(Color.BLACK);
}
}
}
@Override
public void start(Stage stage) {
// Make a tree table view
final TreeTableView<MyTreeItem> treeTableView = new TreeTableView<>();
// Set data for tree table
final MyTreeItem root = new MyTreeItem("Root Node", "This is the root node", null);
final TreeItem<MyTreeItem> rootTreeItem = new TreeItem<>(root);
for (int i = 0; i < 5; i++) {
final MyTreeItem nodeI = new MyTreeItem("Node " + i, "This is node " + i, root);
final TreeItem<MyTreeItem> treeItemI = new TreeItem<>(nodeI);
rootTreeItem.getChildren().add(treeItemI);
for (int j = 0; j < 3; j++) {
final MyTreeItem nodeJ = new MyTreeItem("Node " + i + "." + j, "This is node " + i + "." + j, nodeI);
final TreeItem<MyTreeItem> treeItemJ = new TreeItem<>(nodeJ);
treeItemI.getChildren().add(treeItemJ);
for (int k = 0; k < 10; k++) {
final MyTreeItem nodeK = new MyTreeItem("Node " + i + "." + j + "." + k, "This is node " + i + "." + j + "." + k, nodeJ);
final TreeItem<MyTreeItem> treeItemK = new TreeItem<>(nodeK);
treeItemJ.getChildren().add(treeItemK);
}
}
}
rootTreeItem.setExpanded(true);
// Create a column
final TreeTableColumn<MyTreeItem, MyTreeItem> ttc = new TreeTableColumn<>();
ttc.setCellValueFactory((CellDataFeatures<MyTreeItem, MyTreeItem> p) -> {
final TreeItem<MyTreeItem> treeItem = p.getValue();
return new ReadOnlyObjectWrapper<>(treeItem == null ? null : treeItem.getValue());
});
ttc.setCellFactory(param -> new MyTreeCell());
ttc.setPrefWidth(320);
treeTableView.getColumns().add(ttc);
treeTableView.setRoot(rootTreeItem);
StackPane mainPane = new StackPane(treeTableView);
Scene scene = new Scene(new StackPane(mainPane), 320, 240);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}