我有一个奇怪的复选框选择问题,使用带有自定义CheckBoxTreeCell的CheckBoxTreeItems,在JavaFX 8 TreeView中具有Children的节点.
问题是必须单击带有子项的节点的复选框两次而不是一次才能被选中.叶子只需要一次点击即可.
我的CheckBoxTreeItems采用Person Objects.我重写了CheckBoxTreeCells中的updateItem()方法,将显示的值设置为TreeCell中Person的名称.如果我没有在我的overcover updateItem方法中调用setText(),TreeCell会显示我的Person对象的默认toString()方法(这不是我想要的),并且所有节点在选择它们的复选框时都会表现出预期的行为.
我不想在Person类中更改默认的toString,所以我看到的唯一解决方法是为Person编写一个Wrapper类,在其toString()中返回Persons名称.但我更愿意正确解决这个问题,而不是使用解决方法!
有任何想法吗?非常感谢帮助!
这是我使用的代码:
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
和
public class TreeUtilTest extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
VBox vBox = new VBox();
TreeView<Person> treeView = new TreeView<>();
treeView.setCellFactory(p -> new CheckBoxTreeCell<Person>() {
@Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getName());
}
}
});
vBox.getChildren().add(treeView);
CheckBoxTreeItem<Person> treeRoot = new CheckBoxTreeItem<>();
treeRoot.setValue(new Person("Peter", 10));
treeRoot.setIndependent(true);
treeView.setRoot(treeRoot);
IntStream.range(0, 10).forEach(i -> {
CheckBoxTreeItem<Person> item = new CheckBoxTreeItem<>();
item.setValue(new Person("Friend", i));
item.setIndependent(true);
treeRoot.getChildren().add(item);
});
Scene scene = new Scene(vBox);
primaryStage.setScene(scene);
primaryStage.show();
}
}
解决方法:
如果你看看CheckBoxTreeCell class,你会发现它有可能提供一个返回ObservableValue< Boolean>的回调.具有项目的选择状态和StringConverter.
所以我们可以在课堂上定义这些:
final Callback<TreeItem<Person>, ObservableValue<Boolean>> getSelectedProperty =
(TreeItem<Person> item) -> {
if (item instanceof CheckBoxTreeItem<?>) {
return ((CheckBoxTreeItem<?>)item).selectedProperty();
}
return null;
};
final StringConverter<TreeItem<Person>> converter =
new StringConverter<TreeItem<Person>>() {
@Override
public String toString(TreeItem<Person> object) {
Person item=object.getValue();
return item.getName();
}
@Override
public TreeItem<Person> fromString(String string) {
throw new UnsupportedOperationException("Not supported yet.");
}
};
现在我们只需要使用以下两个参数定义单元工厂:
treeView.setCellFactory(p -> new CheckBoxTreeCell<>(getSelectedProperty,converter));
如果您尝试这样做,您将看到只需单击一下即可选择/取消选择根以及子项.
编辑
有一个更简单的解决方案,使用静态方法forTreeView,您不需要提供回调或转换器:
treeView.setCellFactory(CheckBoxTreeCell.<Person>forTreeView());
但要实现这一点,您只需要在Person中覆盖toString:
private class Person {
...
@Override
public String toString() {
return name;
}
}
这就解释了为什么你首先遇到问题:如果你将toString方法添加到你的代码中,它也可以用你的CheckBoxTreeCell:
treeView.setCellFactory(p -> new CheckBoxTreeCell<Person>() {
@Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getName());
}
}
});