折叠/展开TreeView随机检查CheckBoxes

时间:2017-09-19 11:17:34

标签: java checkbox javafx treeview treeviewitem

我有一个TreeView,其中有几个节点有CheckBoxes(参见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我知道以下内容:

  

由于TreeCell从IndexedCell扩展,每个TreeCell还提供索引属性。索引将在单元格展开和折叠时更新,因此应被视为视图索引而不是模型索引。

这是预期的行为吗?为什么有人想要那个?

1 个答案:

答案 0 :(得分:0)

您看到的行为与单元格的索引无关,而仅仅是因为您没有为CheckBoxTreeCell提供任何机制来“知道”是否应该检查它。因此,当您展开或折叠树中的节点,并且单元格被重用于其他项目时,即使它们现在应该代表新数据,它们也可能会保持其已检查状态。

此处的基本问题是CheckBoxTreeCell只是视图:它不会保持所选状态。您需要为单元格提供一种机制,以了解它所代表的项目是否被选中。 API提供了两种方法:使用CheckBoxTreeItem作为树中的项,或者使用具有BooleanProperty的模型类,并提供该布尔属性的映射。

第一个版本看起来像这样(我也摆脱了所有的原始类型:你真的不应该在这里发布产生警告的代码而只是忽略它们):

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);
    }
}