折叠/展开 TreeView 随机检查 CheckBoxes
Collapsing / expanding TreeView randomly checks CheckBoxes
我有一个 TreeView,其中有几个节点具有复选框(参见 MWE)。
收起/展开某个节点时,其他节点的CheckBox是勾选还是不勾选。
要重现该行为,只需展开所有节点,选中 ChildA
,折叠 Block1
并自动选中 ChildC
。
package treeviewexample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TreeViewExample extends Application {
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
/* example Treeview */
TreeView tw = new TreeView();
TreeItem rootNode = new TreeItem("Root");
TreeItem blockOne = new TreeItem("Block1");
TreeItem childA = new TreeItem("ChildA");
TreeItem childB = new TreeItem("ChildB");
blockOne.getChildren().add(childA);
blockOne.getChildren().add(childB);
TreeItem blockTwo = new TreeItem("Block2");
TreeItem childC = new TreeItem("ChildC");
TreeItem childD = new TreeItem("ChildD");
blockTwo.getChildren().add(childC);
blockTwo.getChildren().add(childD);
rootNode.getChildren().add(blockOne);
rootNode.getChildren().add(blockTwo);
tw.setRoot(rootNode);
/* add CheckBoxes */
tw.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
root.getChildren().add(tw);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
如何防止这种行为?在我的程序的稍后一点,我想通过 TreeView 并获取节点的状态(选中或未选中)以使用它们。
来自 https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeCell.html 我知道以下内容:
Due to the fact that TreeCell extends from IndexedCell, each TreeCell also provides an index property. The index will be updated as cells are expanded and collapsed, and therefore should be considered a view index rather than a model index.
这是有意为之的行为吗?为什么会有人想要那个?
您看到的行为与单元格的索引无关,而仅仅是因为您没有为 CheckBoxTreeCell
到 "know" 提供任何机制,无论它是否应该被检查。因此,当您展开或折叠树中的节点,并且单元格被重新用于其他项目时,即使它们现在应该代表新数据,它们也可能会保持选中状态。
这里的基本问题是 CheckBoxTreeCell
只是 视图 :它不保持选中状态。您需要为单元格提供一种机制,以了解它所代表的项目是否被选中。 API 提供了两种方法来做到这一点:要么使用 CheckBoxTreeItem
作为树中的项目,要么使用具有 BooleanProperty
的模型 class 并提供映射到那个布尔值 属性.
第一个版本看起来像这样(我也摆脱了你所有的原始类型:你真的不应该 post 代码在这里生成警告并忽略它们):
public void start(Stage primaryStage) {
StackPane root = new StackPane();
/* example Treeview */
TreeView<String> tw = new TreeView<>();
CheckBoxTreeItem<String> rootNode = new CheckBoxTreeItem<>("Root");
CheckBoxTreeItem<String> blockOne = new CheckBoxTreeItem<>("Block1");
CheckBoxTreeItem<String> childA = new CheckBoxTreeItem<>("ChildA");
CheckBoxTreeItem<String> childB = new CheckBoxTreeItem<>("ChildB");
blockOne.getChildren().add(childA);
blockOne.getChildren().add(childB);
CheckBoxTreeItem<String> blockTwo = new CheckBoxTreeItem<>("Block2");
CheckBoxTreeItem<String> childC = new CheckBoxTreeItem<>("ChildC");
CheckBoxTreeItem<String> childD = new CheckBoxTreeItem<>("ChildD");
blockTwo.getChildren().add(childC);
blockTwo.getChildren().add(childD);
rootNode.getChildren().add(blockOne);
rootNode.getChildren().add(blockTwo);
tw.setRoot(rootNode);
/* add CheckBoxes */
tw.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
root.getChildren().add(tw);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
第二个选项看起来像:
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
/* example Treeview */
TreeView<Item> tw = new TreeView<>();
CheckBoxTreeItem<Item> rootNode = new CheckBoxTreeItem<>(new Item("Root"));
CheckBoxTreeItem<Item> blockOne = new CheckBoxTreeItem<>(new Item("Block1"));
CheckBoxTreeItem<Item> childA = new CheckBoxTreeItem<>(new Item("ChildA"));
CheckBoxTreeItem<Item> childB = new CheckBoxTreeItem<>(new Item("ChildB"));
blockOne.getChildren().add(childA);
blockOne.getChildren().add(childB);
CheckBoxTreeItem<Item> blockTwo = new CheckBoxTreeItem<>(new Item("Block2"));
CheckBoxTreeItem<Item> childC = new CheckBoxTreeItem<>(new Item("ChildC"));
CheckBoxTreeItem<Item> childD = new CheckBoxTreeItem<>(new Item("ChildD"));
blockTwo.getChildren().add(childC);
blockTwo.getChildren().add(childD);
rootNode.getChildren().add(blockOne);
rootNode.getChildren().add(blockTwo);
tw.setRoot(rootNode);
StringConverter<TreeItem<Item>> itemStringConverter = new StringConverter<TreeItem<Item>>() {
@Override
public String toString(TreeItem<Item> item) {
return item.getValue().getName();
}
@Override
public TreeItem<Item> fromString(String string) {
return new TreeItem<>(new Item(string));
}
};
/* add CheckBoxes */
tw.setCellFactory(
CheckBoxTreeCell.forTreeView(treeItem -> treeItem.getValue().selectedProperty(), itemStringConverter));
root.getChildren().add(tw);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Item {
private final String name;
// use something with a more meaningful name here:
private final BooleanProperty selected = new SimpleBooleanProperty();
public Item(String name, boolean selected) {
this.name = name;
setSelected(selected);
}
public Item(String name) {
this(name, false);
}
public String getName() {
return name;
}
public BooleanProperty selectedProperty() {
return selected;
}
public final boolean isSelected() {
return selectedProperty().get();
}
public final void setSelected(boolean selected) {
selectedProperty().set(selected);
}
}
我有一个 TreeView,其中有几个节点具有复选框(参见 MWE)。
收起/展开某个节点时,其他节点的CheckBox是勾选还是不勾选。
要重现该行为,只需展开所有节点,选中 ChildA
,折叠 Block1
并自动选中 ChildC
。
package treeviewexample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TreeViewExample extends Application {
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
/* example Treeview */
TreeView tw = new TreeView();
TreeItem rootNode = new TreeItem("Root");
TreeItem blockOne = new TreeItem("Block1");
TreeItem childA = new TreeItem("ChildA");
TreeItem childB = new TreeItem("ChildB");
blockOne.getChildren().add(childA);
blockOne.getChildren().add(childB);
TreeItem blockTwo = new TreeItem("Block2");
TreeItem childC = new TreeItem("ChildC");
TreeItem childD = new TreeItem("ChildD");
blockTwo.getChildren().add(childC);
blockTwo.getChildren().add(childD);
rootNode.getChildren().add(blockOne);
rootNode.getChildren().add(blockTwo);
tw.setRoot(rootNode);
/* add CheckBoxes */
tw.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
root.getChildren().add(tw);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
如何防止这种行为?在我的程序的稍后一点,我想通过 TreeView 并获取节点的状态(选中或未选中)以使用它们。
来自 https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeCell.html 我知道以下内容:
Due to the fact that TreeCell extends from IndexedCell, each TreeCell also provides an index property. The index will be updated as cells are expanded and collapsed, and therefore should be considered a view index rather than a model index.
这是有意为之的行为吗?为什么会有人想要那个?
您看到的行为与单元格的索引无关,而仅仅是因为您没有为 CheckBoxTreeCell
到 "know" 提供任何机制,无论它是否应该被检查。因此,当您展开或折叠树中的节点,并且单元格被重新用于其他项目时,即使它们现在应该代表新数据,它们也可能会保持选中状态。
这里的基本问题是 CheckBoxTreeCell
只是 视图 :它不保持选中状态。您需要为单元格提供一种机制,以了解它所代表的项目是否被选中。 API 提供了两种方法来做到这一点:要么使用 CheckBoxTreeItem
作为树中的项目,要么使用具有 BooleanProperty
的模型 class 并提供映射到那个布尔值 属性.
第一个版本看起来像这样(我也摆脱了你所有的原始类型:你真的不应该 post 代码在这里生成警告并忽略它们):
public void start(Stage primaryStage) {
StackPane root = new StackPane();
/* example Treeview */
TreeView<String> tw = new TreeView<>();
CheckBoxTreeItem<String> rootNode = new CheckBoxTreeItem<>("Root");
CheckBoxTreeItem<String> blockOne = new CheckBoxTreeItem<>("Block1");
CheckBoxTreeItem<String> childA = new CheckBoxTreeItem<>("ChildA");
CheckBoxTreeItem<String> childB = new CheckBoxTreeItem<>("ChildB");
blockOne.getChildren().add(childA);
blockOne.getChildren().add(childB);
CheckBoxTreeItem<String> blockTwo = new CheckBoxTreeItem<>("Block2");
CheckBoxTreeItem<String> childC = new CheckBoxTreeItem<>("ChildC");
CheckBoxTreeItem<String> childD = new CheckBoxTreeItem<>("ChildD");
blockTwo.getChildren().add(childC);
blockTwo.getChildren().add(childD);
rootNode.getChildren().add(blockOne);
rootNode.getChildren().add(blockTwo);
tw.setRoot(rootNode);
/* add CheckBoxes */
tw.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
root.getChildren().add(tw);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
第二个选项看起来像:
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
/* example Treeview */
TreeView<Item> tw = new TreeView<>();
CheckBoxTreeItem<Item> rootNode = new CheckBoxTreeItem<>(new Item("Root"));
CheckBoxTreeItem<Item> blockOne = new CheckBoxTreeItem<>(new Item("Block1"));
CheckBoxTreeItem<Item> childA = new CheckBoxTreeItem<>(new Item("ChildA"));
CheckBoxTreeItem<Item> childB = new CheckBoxTreeItem<>(new Item("ChildB"));
blockOne.getChildren().add(childA);
blockOne.getChildren().add(childB);
CheckBoxTreeItem<Item> blockTwo = new CheckBoxTreeItem<>(new Item("Block2"));
CheckBoxTreeItem<Item> childC = new CheckBoxTreeItem<>(new Item("ChildC"));
CheckBoxTreeItem<Item> childD = new CheckBoxTreeItem<>(new Item("ChildD"));
blockTwo.getChildren().add(childC);
blockTwo.getChildren().add(childD);
rootNode.getChildren().add(blockOne);
rootNode.getChildren().add(blockTwo);
tw.setRoot(rootNode);
StringConverter<TreeItem<Item>> itemStringConverter = new StringConverter<TreeItem<Item>>() {
@Override
public String toString(TreeItem<Item> item) {
return item.getValue().getName();
}
@Override
public TreeItem<Item> fromString(String string) {
return new TreeItem<>(new Item(string));
}
};
/* add CheckBoxes */
tw.setCellFactory(
CheckBoxTreeCell.forTreeView(treeItem -> treeItem.getValue().selectedProperty(), itemStringConverter));
root.getChildren().add(tw);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Item {
private final String name;
// use something with a more meaningful name here:
private final BooleanProperty selected = new SimpleBooleanProperty();
public Item(String name, boolean selected) {
this.name = name;
setSelected(selected);
}
public Item(String name) {
this(name, false);
}
public String getName() {
return name;
}
public BooleanProperty selectedProperty() {
return selected;
}
public final boolean isSelected() {
return selectedProperty().get();
}
public final void setSelected(boolean selected) {
selectedProperty().set(selected);
}
}