JavaFX CheckBoxTableCell 拒绝绑定到值
JavaFX CheckBoxTableCell refuses to bind to value
我有一个 TableView,其中有一列应该是布尔值的复选框(JavaFX 16,Java 11),但由于某种原因,该复选框拒绝实际绑定到对象的字段。尝试使用专为布尔列制作的 forTableColumn 静态方法已经失败,我已经尝试扩展 CheckBoxTableColumn 并在其中进行绑定无济于事(尽管我不需要只为基本绑定这样做。
在我的 FXML 控制器中,我正在调用 ascColumn.setCellFactory(CheckBoxTableCell.forTableColumn(ascColumn));
,我的列是
<TableColumn fx:id="ascColumn" text="Asc" prefWidth="$SHORT_CELL_WIDTH">
<cellValueFactory>
<PropertyValueFactory property="ascension"/>
</cellValueFactory>
</TableColumn>
它当然有效,因为复选框出现了,但选中和取消选中实际上并没有到达源对象。没有其他列需要该字段是 ObservableValue,所有其他列都可以自己处理它,所以我正在寻找一种解决方案,源值只是一个常规布尔值。我还尝试将 selectedStateCallback 设置为 return 一个 BooleanProperty,然后我向其添加了一个监听器,但该监听器永远不会被调用。
最终我想要实现的是只有当行的对象满足特定条件时才会出现复选框,为此我制作了一个新的 class 来扩展 CheckBoxTableCell,但是因为我可以'首先无法让默认的工作,我也无法让它工作,所以我需要先解决这个问题。
编辑: 因为我想这还不足以证明问题,这里有一个示例。
控制器:
public class Controller {
@FXML
private TableColumn<TestObject, Boolean> checkColumn;
@FXML
private TableView<TestObject> table;
public void initialize() {
List<TestObject> items = new ArrayList<>();
items.add(new TestObject("test1", false));
items.add(new TestObject("test2", false));
items.add(new TestObject("test3", true));
table.setItems(FXCollections.observableArrayList(items));
checkColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkColumn));
}
}
FXML:
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:controller="Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<TableView fx:id="table" editable="true">
<columns>
<TableColumn text="Name">
<cellValueFactory>
<PropertyValueFactory property="name"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Check" fx:id="checkColumn">
<cellValueFactory>
<PropertyValueFactory property="check"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</GridPane>
主要class:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
@Override
public void stop() throws Exception {
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}
build.gradle:
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.9'
}
group 'org.example'
version '1.0-SNAPSHOT'
mainClassName = 'Main'
repositories {
mavenCentral()
}
test {
useJUnitPlatform()
}
javafx {
version = "16"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
正如我所说,其他一切都有效,不需要依赖已经是属性的输入值,forTableColumn 方法的文档字面上说该列必须是布尔值(不可观察),所以除非我我严重误解了一些东西,它应该有效
[...] so I'm looking for a solution to doing it with the source value being just a regular boolean. [...]
那么据我所知你不能使用CheckBoxTableCell.forTableColumn()
。您可以使用 CheckBox
定义自己的自定义 table 单元格,如下所示:
package org.example;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class App extends Application {
@Override
public void start(Stage stage) {
// Create table and column:
TableView<Item> table = new TableView<>();
TableColumn<Item, Item> // Do not use <Item, Boolean> here!
checkBoxCol = new TableColumn<>("checkBoxCol");
table.getColumns().add(checkBoxCol);
// Some styling:
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
checkBoxCol.setStyle("-fx-alignment: center;");
// Set cell value with the use of SimpleObjectProperty:
checkBoxCol.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue()));
// Create custom cell with the check box control:
checkBoxCol.setCellFactory(tc -> new TableCell<>() {
@Override
protected void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
CheckBox checkBox = new CheckBox();
// Set starting value:
checkBox.setSelected(item.isSelected());
// Add listener!!!:
checkBox.selectedProperty().addListener((observable, oldValue, newValue) ->
item.setSelected(newValue));
setGraphic(checkBox);
}
}
});
// Print items to see if the selected value gets updated:
Button printBtn = new Button("Print Items");
printBtn.setOnAction(event -> {
System.out.println("---");
table.getItems().forEach(System.out::println);
});
// Add test data:
IntStream.range(0, 3).forEach(i -> table.getItems().add(new Item()));
// Prepare and show stage:
stage.setScene(new Scene(new VBox(table, printBtn), 100, 200));
stage.show();
}
/**
* Simple class with just one "regular boolean" property.
*/
public class Item {
private boolean selected = false;
@Override
public String toString() {
return Item.class.getSimpleName()
+ "[selected=" + selected + "]";
}
// Getters & Setters:
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
public static void main(String[] args) {
launch();
}
}
Ultimately what I want to achieve is for the checkboxes to only appear
if the object of the row meets certain conditions [...]
然后您可以在 updateItem()
方法中添加一个 if 条件,例如 e。 g.:
[...]
if (item == null || empty) {
setGraphic(null);
} else {
if (showCheckBox) {
CheckBox checkBox = new CheckBox();
[...]
setGraphic(checkBox);
} else
setGraphic(null);
}
[...]
但我认为最好使用可观察的属性。如果你不想触及原来的 class,你可以创建一个包装器 class.
我有一个 TableView,其中有一列应该是布尔值的复选框(JavaFX 16,Java 11),但由于某种原因,该复选框拒绝实际绑定到对象的字段。尝试使用专为布尔列制作的 forTableColumn 静态方法已经失败,我已经尝试扩展 CheckBoxTableColumn 并在其中进行绑定无济于事(尽管我不需要只为基本绑定这样做。
在我的 FXML 控制器中,我正在调用 ascColumn.setCellFactory(CheckBoxTableCell.forTableColumn(ascColumn));
,我的列是
<TableColumn fx:id="ascColumn" text="Asc" prefWidth="$SHORT_CELL_WIDTH">
<cellValueFactory>
<PropertyValueFactory property="ascension"/>
</cellValueFactory>
</TableColumn>
它当然有效,因为复选框出现了,但选中和取消选中实际上并没有到达源对象。没有其他列需要该字段是 ObservableValue,所有其他列都可以自己处理它,所以我正在寻找一种解决方案,源值只是一个常规布尔值。我还尝试将 selectedStateCallback 设置为 return 一个 BooleanProperty,然后我向其添加了一个监听器,但该监听器永远不会被调用。
最终我想要实现的是只有当行的对象满足特定条件时才会出现复选框,为此我制作了一个新的 class 来扩展 CheckBoxTableCell,但是因为我可以'首先无法让默认的工作,我也无法让它工作,所以我需要先解决这个问题。
编辑: 因为我想这还不足以证明问题,这里有一个示例。
控制器:
public class Controller {
@FXML
private TableColumn<TestObject, Boolean> checkColumn;
@FXML
private TableView<TestObject> table;
public void initialize() {
List<TestObject> items = new ArrayList<>();
items.add(new TestObject("test1", false));
items.add(new TestObject("test2", false));
items.add(new TestObject("test3", true));
table.setItems(FXCollections.observableArrayList(items));
checkColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkColumn));
}
}
FXML:
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:controller="Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<TableView fx:id="table" editable="true">
<columns>
<TableColumn text="Name">
<cellValueFactory>
<PropertyValueFactory property="name"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Check" fx:id="checkColumn">
<cellValueFactory>
<PropertyValueFactory property="check"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</GridPane>
主要class:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
@Override
public void stop() throws Exception {
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}
build.gradle:
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.9'
}
group 'org.example'
version '1.0-SNAPSHOT'
mainClassName = 'Main'
repositories {
mavenCentral()
}
test {
useJUnitPlatform()
}
javafx {
version = "16"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
正如我所说,其他一切都有效,不需要依赖已经是属性的输入值,forTableColumn 方法的文档字面上说该列必须是布尔值(不可观察),所以除非我我严重误解了一些东西,它应该有效
[...] so I'm looking for a solution to doing it with the source value being just a regular boolean. [...]
那么据我所知你不能使用CheckBoxTableCell.forTableColumn()
。您可以使用 CheckBox
定义自己的自定义 table 单元格,如下所示:
package org.example;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class App extends Application {
@Override
public void start(Stage stage) {
// Create table and column:
TableView<Item> table = new TableView<>();
TableColumn<Item, Item> // Do not use <Item, Boolean> here!
checkBoxCol = new TableColumn<>("checkBoxCol");
table.getColumns().add(checkBoxCol);
// Some styling:
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
checkBoxCol.setStyle("-fx-alignment: center;");
// Set cell value with the use of SimpleObjectProperty:
checkBoxCol.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue()));
// Create custom cell with the check box control:
checkBoxCol.setCellFactory(tc -> new TableCell<>() {
@Override
protected void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
CheckBox checkBox = new CheckBox();
// Set starting value:
checkBox.setSelected(item.isSelected());
// Add listener!!!:
checkBox.selectedProperty().addListener((observable, oldValue, newValue) ->
item.setSelected(newValue));
setGraphic(checkBox);
}
}
});
// Print items to see if the selected value gets updated:
Button printBtn = new Button("Print Items");
printBtn.setOnAction(event -> {
System.out.println("---");
table.getItems().forEach(System.out::println);
});
// Add test data:
IntStream.range(0, 3).forEach(i -> table.getItems().add(new Item()));
// Prepare and show stage:
stage.setScene(new Scene(new VBox(table, printBtn), 100, 200));
stage.show();
}
/**
* Simple class with just one "regular boolean" property.
*/
public class Item {
private boolean selected = false;
@Override
public String toString() {
return Item.class.getSimpleName()
+ "[selected=" + selected + "]";
}
// Getters & Setters:
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
public static void main(String[] args) {
launch();
}
}
Ultimately what I want to achieve is for the checkboxes to only appear if the object of the row meets certain conditions [...]
然后您可以在 updateItem()
方法中添加一个 if 条件,例如 e。 g.:
[...]
if (item == null || empty) {
setGraphic(null);
} else {
if (showCheckBox) {
CheckBox checkBox = new CheckBox();
[...]
setGraphic(checkBox);
} else
setGraphic(null);
}
[...]
但我认为最好使用可观察的属性。如果你不想触及原来的 class,你可以创建一个包装器 class.