我正在尝试在JavaFX中设置一个复选框树,其中树叶与父母绑定:
我是使用与selectedProperty
的绑定来完成的。
它工作正常,但每次选择或取消选择父级时都会抛出异常。
有一个简单的方法吗?我讨厌放弃一种有效的方法,除了那些令人讨厌的例外情况。
相关代码如下。请原谅任何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);
}
}
任何建议都将不胜感激。
答案 0 :(得分:2)
问题来自这一行
itemColumn.selectedProperty().bind(item.selectedProperty());
要了解为什么在选择的节点中出现异常,您应该查看CheckBoxTreeItem
的{{3}}:
CheckBoxTreeItem可以是独立的或依赖的。 默认情况下, CheckBoxTreeItem实例是依赖的,这意味着任何更改 到TreeItem的选择状态将对父级和 子CheckBoxTreeItem实例。如果CheckBoxTreeItem设置为 独立,这意味着对CheckBoxTreeItem的任何更改 不会直接影响父母和子女的状况 CheckBoxTreeItem实例。
这意味着,当您选择/取消选择具有子项的树中的节点时,它将选择/取消选择其所有子项默认(并设置已选中,未经检查,其父母的不确定状态,但这在您的情况下并不重要)。这是您需要的功能之一,您可以开箱即用。
现在您可以理解为什么绑定存在问题:
您将子节点的selectedProperty()
绑定到父节点的selectedProperty()
,然后:
如果您检查其中一个叶节点,您将收到异常,因为该值已绑定,并且无法设置绑定值。
如果您检查其中一个父节点,它将尝试选择其所有子节点(因为依赖),那么您将再次获得异常。
<强>解决方案强>
您有两个要求:
选择所有子节点,当选择父节点时,已经完成
不允许手动选择子节点
对于第二个要求,您可以禁用这些项目的复选框。为此,您可以使用TreeView
的{{3}}方法:
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());
}
}
};
});
此代码段会将显示的disableProperty
个对象的CheckBoxTreeCell
绑定到相应CheckBoxTreeItem
的{{3}}。
public final ReadOnlyBooleanProperty leafProperty()
表示TreeItem叶属性,如果TreeItem为true 没有孩子。
这会导致树中所有没有子节点的节点都被禁用。
要使其正常工作,您应该从代码中删除此行:
itemColumn.selectedProperty().bind(item.selectedProperty());
你应该替换这一行
MainTree.setCellFactory(p-> new CheckBoxTreeCell());
上面张贴的代码。