JavaFX CheckboxTreeItem:无法选择的叶子
JavaFX CheckboxTreeItem: unselectable leaves
我正在尝试在 JavaFX 中设置一个复选框树,其中叶子绑定到它们的 parents:
- 当 parent 被 selected
时,它们被 selected
- 无法单独更改。
我使用 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 的任何节点都会出现异常,您应该查看 CheckBoxTreeItem
的 independentProperty:
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
绑定到相应 CheckBoxTreeItem
的 leafProperty。
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());
使用上面发布的代码。
我正在尝试在 JavaFX 中设置一个复选框树,其中叶子绑定到它们的 parents:
- 当 parent 被 selected 时,它们被 selected
- 无法单独更改。
我使用 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 的任何节点都会出现异常,您应该查看 CheckBoxTreeItem
的 independentProperty:
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
:
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
绑定到相应 CheckBoxTreeItem
的 leafProperty。
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());
使用上面发布的代码。