提问人:Michael Sims 提问时间:11/12/2023 最后编辑:Michael Sims 更新时间:11/14/2023 访问量:50
无法弄清楚为什么 TreeView 在 JavaFX 中构建为镜像
Cannot figure out why a TreeView builds as a mirror in JavaFX
问:
我在 JavaFX 中的 TreeView 遇到了一个奇怪的问题。
我使用它的应用程序从基于 Web 的数据构建分支,因此填充 TreeView 需要一些时间。但是,当它填充时,分支显示在顶部和底部,但在底部,它只是文本,而不是实际的 TreeItems。
如果有人想自己尝试,我创建了最小的可重现代码。我将最少的代码发布到 GitHub,您可以从这里克隆它。
此屏幕截图还将显示问题。
以下是将重现该问题的类:
主要:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
private TreeItem<TreeNode> root;
private Random random = new Random(System.currentTimeMillis());
@Override
public void start(Stage stage) throws Exception {
root = new TreeItem<>(new TreeNode("Root", false));
TreeView<TreeNode> treeView = new TreeView<>(root);
treeView.setCellFactory(param -> new CellFactory());
treeView.setShowRoot(false);
ScrollPane scrollPane = new ScrollPane(treeView);
double width = 500;
double height = 800;
AnchorPane ap = new AnchorPane(scrollPane);
Scene scene = new Scene(ap);
stage.setScene(scene);
stage.setWidth(width);
stage.setHeight(height);
stage.setOnCloseRequest(e->{
Platform.exit();
System.exit(0);
});
treeView.setPrefWidth(width);
treeView.setPrefHeight(height);
ap.setPrefWidth(width);
ap.setPrefHeight(height);
AnchorPane.setRightAnchor(scrollPane, 0.0);
AnchorPane.setLeftAnchor(scrollPane, 0.0);
AnchorPane.setTopAnchor(scrollPane, 30.0);
AnchorPane.setBottomAnchor(scrollPane, 0.0);
stage.show();
new Thread(buildTree()).start();
}
private String getName() {
int low = 100;
int high = 9999;
return "Node(" + random.nextInt(low, high) + ")";
}
private Runnable buildTree() {
return ()->{
for (int x = 0; x < 100; x++) {
boolean checkBoxNode = x % 2 == 0;
root.getChildren().add(new TreeItem<>(new TreeNode(getName(), checkBoxNode)));
sleep(500);
}
};
}
private void sleep(long time) {
try {
TimeUnit.MILLISECONDS.sleep(time);
}
catch (InterruptedException ignored) {
}
}
}
细胞工厂:
public class CellFactory extends TreeCell<TreeNode> {
private CheckBox checkBox;
public CellFactory() {
checkBox = new CheckBox();
checkBox.setOnAction(event -> {
TreeNode item = getItem();
if (item != null) {
item.setSelected(checkBox.isSelected());
}
});
itemProperty().addListener(((observable, oldValue, treeNode) -> {
if (treeNode != null) {
checkBox.setVisible(treeNode.isCheckBoxNode());
}
}));
}
@Override
protected void updateItem(TreeNode treeNode, boolean empty) {
super.updateItem(treeNode, empty);
if (empty || treeNode == null) {
setGraphic(null);
}
else {
checkBox.setSelected(treeNode.isSelected());
setText(treeNode.toString());
setGraphic(checkBox);
}
}
}
树节点:
public class TreeNode {
private String name;
private boolean selected = true;
private boolean checkBoxNode;
public TreeNode(String name, boolean checkBoxNode) {
this.name = name;
this.checkBoxNode = checkBoxNode;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public boolean isCheckBoxNode() {
return checkBoxNode;
}
@Override
public String toString() {
return name;
}
}
有谁知道为什么会这样?
答:
代码中的 bug 导致观察到的问题
单元格是重复使用的,并且必须在调用中正确更新。updateItem
当单元格不为空时,可以设置单元格的文本。但是,如果以前为空的单元格更新为空值,则不会取消设置文本(将其设置为 null)。
请参阅下面的固定代码:
protected void updateItem(TreeNode treeNode, boolean empty) {
super.updateItem(treeNode, empty);
if (empty || treeNode == null) {
setText(null); // <-- text for previous associated
// item needs to be cleared for empty cells.
setGraphic(null);
} else {
checkBox.setSelected(treeNode.isSelected());
setText(treeNode.toString()); // <--- text has been set here.
setGraphic(checkBox);
}
}
示例中不相关的问题或令人困惑的事情
切勿从其他线程访问或修改与活动场景图关联的元素,而应使用该元素在 JavaFX 线程上添加新的 TreeItems。这不是问题的根源,但这是一个应该解决的问题。Platform.runLater
你的 CellFactory 名称令人困惑,它不是一个单元工厂,它是一个 Cell(你可以看到,因为它扩展了 TreeCell)。工厂是生成新单元格的 lambda 的完整回调。
TreeNode 类的名称令人困惑,它不是 JavaFX 节点。是的,它是数据模型中树的一个节点,但考虑到上下文,这让我感到非常困惑。对其他人来说可能没问题,我不知道。
常见问题
我不完全理解为什么以前的 TreeItem 中的文本会显示在 TreeView 的底部。
单元的创建和放置是内部实现的一部分。除非您想更改 JavaFX 库中的内部实现(您不想这样做),否则您不必了解内部细节是如何工作的。理解这一点的唯一方法是查看代码并从实现中对设计进行逆向工程。TreeView
TreeView
只需确保在支持单元格的项发生更改时正确更新单元格视觉对象。
我想,在内部,TreeView 可能正在移动单元格;有时创建新的,有时重用现有的。但它如何管理细胞并不重要;只要正确更新单元格,就会显示正确的值。
在您的问题示例中发生的奇怪行为是由于代码中的错误而不是 JavaFX 实现中的错误。TreeView
另外,你会用哪个类名而不是 和 ?
CellFactory
TreeNode
可能与您的问题域有关(我不知道)。如果你想要一些非常通用的名字,那么:
CellFactory
->CustomTreeCell
或CheckManagementTreeCell
TreeNode
->TreeItemData
.
评论
ItemClass
TreeNode
ItemClass
ItemCategory
评论