JavaFX 使用自定义 Listview 将 CheckBox 与 setCellFactory 结合使用
JavaFX Using Custom Listview to using CheckBox with setCellFactory
当我阅读 JavaFX 书籍时,他们使用 TodoItem class (1)
final class TodoItem {
final String name;
final BooleanProperty isDone = new SimpleBooleanProperty(false);
public TodoItem(String name) {
this.name = name;
}
@Override
public String toString() {
return name + (isDone.get() ? " DONE" : "");
}
}
和 ObservableList (2)
ObservableList<TodoItem> items = FXCollections.observableArrayList((TodoItem item)
-> { return new Observable[] { item.isDone }; });
和细胞工厂 (3)
list.setCellFactory(CheckBoxListCell.forListView( item -> item.isDone ));
显示程序如下:
当我选中复选框时,复选框旁边的文本将添加“完成”。
当我取消选中复选框时,“完成”将消失。
主要内容如下
我想写另一个版本的 (3) like
list.setCellFactory((ListView<TodoItem> param) -> {
return new ListCell<> () {
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (!(empty || item == null)) {
CheckBox checkBox = new CheckBox();
item.isDone.bind(checkBox.selectedProperty());
setGraphic(checkBox);
setText(item.name);
} else {
setGraphic(null);
setText(null);
}
}
};
});
我创建了一个 CheckBox,我将 isDone 属性 绑定到 CheckBox。
而且我认为,isDone 是可观察的,并且 isDone 与 CheckBox 绑定,
当我检查一个复选框时,它应该使 isDone 为真,并用“DONE”更新文本。
请帮我找出我的错误,并帮我解决我的问题,非常感谢。
如果我没理解错的话,您不想使用 CheckBoxListCell,而是想编写自己的单元工厂。您需要在逻辑中做的主要更改是不要在 updateItem 调用中创建新的 CheckBox。您需要为每个单元格设置一个 CheckBox。因此,为此您需要在 class 级别上声明一个 CheckBox。
下面是实现代码以获得所需功能的一种方法。
listView.setCellFactory((ListView<TodoItem> param) -> new ListCell<TodoItem>() {
private CheckBox checkBox = new CheckBox();
private TodoItem itemBkp;
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (itemBkp != null) {
itemBkp.isDoneProperty().unbindBidirectional(checkBox.selectedProperty());
}
if (!(empty || item == null)) {
item.isDoneProperty().bindBidirectional(checkBox.selectedProperty());
setGraphic(checkBox);
setText(item.toString());
itemBkp = item;
} else {
setGraphic(null);
setText(null);
itemBkp = null;
}
}
});
更新:
如评论中所述,上面提供的代码中存在一个小错误。所以我重新实施了。下面的代码应该解决评论中提到的问题。在这种方法中,我们不需要任何项目备份,因为我们没有绑定任何属性。
listView.setCellFactory((ListView<TodoItem> param) -> new ListCell<TodoItem>() {
private CheckBox checkBox;
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (!(empty || item == null)) {
getCheckBox().setSelected(item.isDoneProperty().getValue());
setGraphic(getCheckBox());
setText(item.toString());
} else {
setGraphic(null);
setText(null);
}
}
private CheckBox getCheckBox(){
if(checkBox==null){
checkBox = new CheckBox();
checkBox.selectedProperty().addListener((obs,old,val)->{
if(getItem()!=null){
getItem().isDoneProperty().setValue(val);
}
});
}
return checkBox;
}
});
下面是完整的demo代码:
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ListViewCheckBoxDemo extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
ListView<TodoItem> listView = new ListView<>();
listView.setCellFactory((ListView<TodoItem> param) -> new ListCell<TodoItem>() {
private CheckBox checkBox;
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (!(empty || item == null)) {
getCheckBox().setSelected(item.isDoneProperty().getValue());
setGraphic(getCheckBox());
setText(item.toString());
} else {
setGraphic(null);
setText(null);
}
}
private CheckBox getCheckBox() {
if (checkBox == null) {
checkBox = new CheckBox();
checkBox.selectedProperty().addListener((obs, old, val) -> {
if (getItem() != null) {
getItem().isDoneProperty().setValue(val);
}
});
}
return checkBox;
}
});
ObservableList<TodoItem> items = FXCollections.observableArrayList(item -> new Observable[]{item.isDone});
items.addAll(new TodoItem("Sign a Contract"), new TodoItem("Fail Deadline"), new TodoItem("Blame Yourself"), new TodoItem("Suffer"));
for (int i = 1; i < 20; i++) {
items.add(new TodoItem("Testing " + i));
}
listView.setItems(items);
CheckBox cb = new CheckBox("Mark 4th item as done (external updating)");
cb.selectedProperty().addListener((obs, old, val) -> {
listView.getItems().get(3).isDoneProperty().setValue(val);
});
VBox root = new VBox(cb, listView);
root.setSpacing(10);
root.setPadding(new Insets(10));
Scene scene = new Scene(root, 350, 250);
stage.setScene(scene);
stage.setTitle("ListView CheckBox Demo");
stage.show();
}
final class TodoItem {
private final String name;
private final BooleanProperty isDone = new SimpleBooleanProperty(false);
public TodoItem(String name) {
this.name = name;
}
@Override
public String toString() {
return name + (isDone.get() ? " DONE" : "");
}
public BooleanProperty isDoneProperty() {
return isDone;
}
}
}
当我阅读 JavaFX 书籍时,他们使用 TodoItem class (1)
final class TodoItem {
final String name;
final BooleanProperty isDone = new SimpleBooleanProperty(false);
public TodoItem(String name) {
this.name = name;
}
@Override
public String toString() {
return name + (isDone.get() ? " DONE" : "");
}
}
和 ObservableList (2)
ObservableList<TodoItem> items = FXCollections.observableArrayList((TodoItem item)
-> { return new Observable[] { item.isDone }; });
和细胞工厂 (3)
list.setCellFactory(CheckBoxListCell.forListView( item -> item.isDone ));
显示程序如下:
当我选中复选框时,复选框旁边的文本将添加“完成”。
当我取消选中复选框时,“完成”将消失。
主要内容如下
我想写另一个版本的 (3) like
list.setCellFactory((ListView<TodoItem> param) -> {
return new ListCell<> () {
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (!(empty || item == null)) {
CheckBox checkBox = new CheckBox();
item.isDone.bind(checkBox.selectedProperty());
setGraphic(checkBox);
setText(item.name);
} else {
setGraphic(null);
setText(null);
}
}
};
});
我创建了一个 CheckBox,我将 isDone 属性 绑定到 CheckBox。
而且我认为,isDone 是可观察的,并且 isDone 与 CheckBox 绑定,
当我检查一个复选框时,它应该使 isDone 为真,并用“DONE”更新文本。
请帮我找出我的错误,并帮我解决我的问题,非常感谢。
如果我没理解错的话,您不想使用 CheckBoxListCell,而是想编写自己的单元工厂。您需要在逻辑中做的主要更改是不要在 updateItem 调用中创建新的 CheckBox。您需要为每个单元格设置一个 CheckBox。因此,为此您需要在 class 级别上声明一个 CheckBox。
下面是实现代码以获得所需功能的一种方法。
listView.setCellFactory((ListView<TodoItem> param) -> new ListCell<TodoItem>() {
private CheckBox checkBox = new CheckBox();
private TodoItem itemBkp;
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (itemBkp != null) {
itemBkp.isDoneProperty().unbindBidirectional(checkBox.selectedProperty());
}
if (!(empty || item == null)) {
item.isDoneProperty().bindBidirectional(checkBox.selectedProperty());
setGraphic(checkBox);
setText(item.toString());
itemBkp = item;
} else {
setGraphic(null);
setText(null);
itemBkp = null;
}
}
});
更新:
如评论中所述,上面提供的代码中存在一个小错误。所以我重新实施了。下面的代码应该解决评论中提到的问题。在这种方法中,我们不需要任何项目备份,因为我们没有绑定任何属性。
listView.setCellFactory((ListView<TodoItem> param) -> new ListCell<TodoItem>() {
private CheckBox checkBox;
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (!(empty || item == null)) {
getCheckBox().setSelected(item.isDoneProperty().getValue());
setGraphic(getCheckBox());
setText(item.toString());
} else {
setGraphic(null);
setText(null);
}
}
private CheckBox getCheckBox(){
if(checkBox==null){
checkBox = new CheckBox();
checkBox.selectedProperty().addListener((obs,old,val)->{
if(getItem()!=null){
getItem().isDoneProperty().setValue(val);
}
});
}
return checkBox;
}
});
下面是完整的demo代码:
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ListViewCheckBoxDemo extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
ListView<TodoItem> listView = new ListView<>();
listView.setCellFactory((ListView<TodoItem> param) -> new ListCell<TodoItem>() {
private CheckBox checkBox;
@Override
public void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
if (!(empty || item == null)) {
getCheckBox().setSelected(item.isDoneProperty().getValue());
setGraphic(getCheckBox());
setText(item.toString());
} else {
setGraphic(null);
setText(null);
}
}
private CheckBox getCheckBox() {
if (checkBox == null) {
checkBox = new CheckBox();
checkBox.selectedProperty().addListener((obs, old, val) -> {
if (getItem() != null) {
getItem().isDoneProperty().setValue(val);
}
});
}
return checkBox;
}
});
ObservableList<TodoItem> items = FXCollections.observableArrayList(item -> new Observable[]{item.isDone});
items.addAll(new TodoItem("Sign a Contract"), new TodoItem("Fail Deadline"), new TodoItem("Blame Yourself"), new TodoItem("Suffer"));
for (int i = 1; i < 20; i++) {
items.add(new TodoItem("Testing " + i));
}
listView.setItems(items);
CheckBox cb = new CheckBox("Mark 4th item as done (external updating)");
cb.selectedProperty().addListener((obs, old, val) -> {
listView.getItems().get(3).isDoneProperty().setValue(val);
});
VBox root = new VBox(cb, listView);
root.setSpacing(10);
root.setPadding(new Insets(10));
Scene scene = new Scene(root, 350, 250);
stage.setScene(scene);
stage.setTitle("ListView CheckBox Demo");
stage.show();
}
final class TodoItem {
private final String name;
private final BooleanProperty isDone = new SimpleBooleanProperty(false);
public TodoItem(String name) {
this.name = name;
}
@Override
public String toString() {
return name + (isDone.get() ? " DONE" : "");
}
public BooleanProperty isDoneProperty() {
return isDone;
}
}
}