JavaFX CheckboxTreeItem:无法选择的叶子

JavaFX CheckboxTreeItem: unselectable leaves

我正在尝试在 JavaFX 中设置一个复选框树,其中叶子绑定到它们的 parents:

我使用 selectedProperty 的绑定完成了它。

它工作得很好,但每次我 select 或 deselect parent.

时它都会抛出异常

有没有简单的方法解决这个问题?我讨厌放弃一种除了那些讨厌的例外之外有效的方法。

相关代码如下。请原谅任何 java 约定错误 - 我对这门语言还是陌生的。

控制器:

package application;

import java.util.ArrayList;
import java.util.List;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;

public class BindingTestController {

@FXML
private TreeView<String> MainTree;


static List<CheckBoxTreeItem<String>> treeItems = new ArrayList<CheckBoxTreeItem<String>>();


CheckBoxTreeItem<String> root = new CheckBoxTreeItem<String>("Root");
CheckBoxTreeItem<String> parent1 = new CheckBoxTreeItem<String>("Parent1");
CheckBoxTreeItem<String> parent2 = new CheckBoxTreeItem<String>("Parent2");


private void AddColumns(CheckBoxTreeItem<String> item){

    for (int i = 1 ; i < 5 ; i++){
        CheckBoxTreeItem<String> itemColumn = new CheckBoxTreeItem<String>(item.getValue()+"_Column_"+i);
        treeItems.add(itemColumn);
        item.getChildren().add(itemColumn);

        itemColumn.selectedProperty().bind(item.selectedProperty());

    }
}




@FXML // This method is called by the FXMLLoader when initialization is complete
private void initialize() {

    treeItems.add(root);
    treeItems.add(parent1);
    root.getChildren().add(parent1);
    treeItems.add(parent2);
    root.getChildren().add(parent2);

    AddColumns(parent1);
    AddColumns(parent2);

    MainTree.setRoot(root);
    MainTree.setShowRoot(true);
    root.setExpanded(true);

       MainTree.setCellFactory(p-> new CheckBoxTreeCell()); 

      MainTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
     MainTree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observable, Object oldValue, Object newValue) {

                CheckBoxTreeItem<String> treeItem = (CheckBoxTreeItem)newValue;

            }
        });


}


}

这是 FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>

<BorderPane xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.BindingTestController">
   <center>
      <VBox prefHeight="1049.0" prefWidth="714.0"BorderPane.alignment="CENTER">
         <children>
            <TreeView fx:id="MainTree" prefHeight="624.0" prefWidth="714.0"/>
         </children>
      </VBox>
   </center>
 </BorderPane>

这里是主要的:

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;


public class Main extends Application {
@Override
public void start(Stage primaryStage) {
    try {
        //          BorderPane root = new BorderPane();
        BorderPane root = (BorderPane) FXMLLoader.load(Main.class.getResource("BindingTest.fxml"));
        Scene scene = new Scene(root,400,400);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    launch(args);
    }
}

如有任何建议,我们将不胜感激。

问题出在这一行

itemColumn.selectedProperty().bind(item.selectedProperty());

要了解为什么在 select 的任何节点都会出现异常,您应该查看 CheckBoxTreeItemindependentProperty:

A CheckBoxTreeItem can be independent or dependent. By default, CheckBoxTreeItem instances are dependent, which means that any changes to the selection state of a TreeItem will have an impact on parent and children CheckBoxTreeItem instances. If a CheckBoxTreeItem is set to be independent, this means that any changes to that CheckBoxTreeItem will not directly impact the state of parent and children CheckBoxTreeItem instances.

这意味着,当您 select/deselect 树中的一个节点有子节点时,它将 select/deselect 它的所有子节点 默认情况下 (并且还设置其父级的选中、未选中、不确定状态,但这在您的情况下并不重要)。这是您需要的功能之一,开箱即用。

现在您可以理解为什么您的绑定有问题了: 你把子节点的selectedProperty()绑定到父节点的selectedProperty(),那么:

  • 如果检查其中一个叶子节点,会出现异常,因为该值是绑定的,不能设置绑定值

  • 如果你检查其中一个父节点,它会尝试select它的所有子节点(因为依赖),然后你会再次得到一个异常。

解决方案

您有两个要求:

  • select 所有的子节点,当父节点被 selected 时,已经完成了

  • 不允许手动 select 子节点

对于第二个要求,您可以禁用这些项目的复选框。为此,您可以使用 TreeView:

setCellFactory 方法
MainTree.setCellFactory((item) -> {
    return new CheckBoxTreeCell<String>(){

        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            if(item != null) {
                this.disableProperty().unbind();
                CheckBoxTreeItem<String> value = (CheckBoxTreeItem<String>) treeItemProperty().getValue();
                this.disableProperty().bind(value.leafProperty());
            }
        }
    };
});

此代码段会将显示的 CheckBoxTreeCell 对象的 disableProperty 绑定到相应 CheckBoxTreeItemleafProperty

public final ReadOnlyBooleanProperty leafProperty()

Represents the TreeItem leaf property, which is true if the TreeItem has no children.

这会导致树中所有没有子节点的节点都被禁用。

要使其正常工作,您应该从代码中删除这一行:

itemColumn.selectedProperty().bind(item.selectedProperty());

你应该替换这一行

MainTree.setCellFactory(p-> new CheckBoxTreeCell()); 

使用上面发布的代码。