2017-09-19 42 views
0

我有一個具有CheckBoxes的幾個節點的TreeView(請參閱MWE)。摺疊/展開TreeView隨機檢查複選框

當摺疊/展開某個節點時,其他節點的複選框被選中或取消選中。

重現行爲,只需展開所有節點,檢查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屬性。索引將隨着單元格展開和摺疊而更新,因此應被視爲視圖索引而不是模型索引。

那麼這是預期的行爲?爲什麼有人想要那樣?

回答

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