【问题标题】:Collapsing / expanding TreeView randomly checks CheckBoxes折叠/展开 TreeView 随机检查 CheckBoxes
【发布时间】:2018-02-28 03:48:21
【问题描述】:

我有一个 TreeView,其中有几个节点具有 CheckBoxes(请参阅 MWE)。

当折叠/展开某个节点时,其他节点的复选框被选中或取消选中。

要重现该行为,只需展开所有节点,检查ChildA,折叠Block1ChildC 将被自动检查。

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 还提供了一个 index 属性。该索引将随着单元格的展开和折叠而更新,因此应将其视为视图索引而不是模型索引。

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

【问题讨论】:

    标签: java checkbox javafx treeview treeviewitem


    【解决方案1】:

    您看到的行为与单元格的索引无关,而仅仅是因为您没有为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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-12-18
      • 2012-04-24
      • 2017-02-21
      • 1970-01-01
      • 1970-01-01
      • 2012-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多