我想实现一个解决方案,避免使用 PropertyValueFactory,并且还能够在 TableCell updateItem 方法中引用所有类的属性,而不仅仅是绑定属性显示值。
注:
我专门将 col_2 声明为 public TableColumn
这允许类的所有属性在单元格 updateItem 方法中可用。 正如您将看到的,单元格 2 的显示输出是 2 个属性的组合。
按下设置按钮会显示此内容
按下 GO 按钮会更改 List origlist 的 fld2 属性值。
按下中断按钮可向控制台确认 List origlist 中的值已更改。
但是,更改不会传播到显示屏。
使用 PropertyValueFactory 设置的列会更新。
总而言之,第 2 列所做的事情除了无法观察到绑定数据的变化之外。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox alignment="CENTER" prefHeight="473.0" prefWidth="923.0" spacing="20.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.test.tableview.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<TableView fx:id="TableView1" editable="true" prefHeight="200.0" prefWidth="400.0">
<columns>
<TableColumn fx:id="col_1" prefWidth="200.0" text="C1" />
<TableColumn fx:id="col_2" prefWidth="200.0" text="C2" />
<TableColumn fx:id="col_3" prefWidth="200.0" text="C3" />
<TableColumn fx:id="col_4" prefWidth="200.0" text="C4" />
</columns>
</TableView>
<Button onAction="#onsetup" text="setup" />
<Button onAction="#ongoClick" text="GO" />
<Button onAction="#onbreak" text="break" />
</VBox>
package org.test.tableview;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class myclass
{
private IntegerProperty fld1 = new SimpleIntegerProperty();
private StringProperty fld2 = new SimpleStringProperty();
private StringProperty fld3 = new SimpleStringProperty();
private StringProperty fld4 = new SimpleStringProperty();
public myclass(Integer fld1,String fld2,String fld3,String fld4)
{
this.fld1.set(fld1);
this.fld2.set(fld2);
this.fld3.set(fld3);
this.fld4.set(fld4);
}
// Getter methods
public IntegerProperty fld1Property() {
return fld1;
}
public StringProperty fld2Property() {
return fld2;
}
public StringProperty fld3Property() {
return fld3;
}
public StringProperty fld4Property() {
return fld4;
}
}
package org.test.tableview;
import javafx.beans.InvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import java.util.ArrayList;
import java.util.List;
public class HelloController
{
public TableView<myclass> TableView1;
public TableColumn<myclass,Integer> col_1;
public TableColumn<myclass,myclass> col_2;
public TableColumn<myclass,String> col_3;
public TableColumn<myclass,String> col_4;
List<myclass> origlist = new ArrayList<>();
public HelloController()
{
init();
}
private void init()
{
origlist.clear();
myclass m = new myclass(1,"A1","B1","C1");
origlist.add(m);
m= new myclass(2,"A2","B2","C2");
origlist.add(m);
m= new myclass(3,"A3","B3","C3");
origlist.add(m);
m= new myclass(4,"A4","B4","C4");
origlist.add(m);
}
@FXML
public void onsetup(ActionEvent actionEvent)
{
init();
col_1.setCellValueFactory(new PropertyValueFactory<>("fld1"));
col_2.setCellValueFactory(data -> new ObservableValue<>()
{
@Override
public void addListener(ChangeListener<? super myclass> listener) {}
@Override
public void removeListener(ChangeListener<? super myclass> listener) {}
@Override
public myclass getValue()
{
return data.getValue();
}
@Override
public void addListener(InvalidationListener listener) {}
@Override
public void removeListener(InvalidationListener listener) {
}
});
col_3.setCellValueFactory(new PropertyValueFactory<>("fld3"));
col_4.setCellValueFactory(new PropertyValueFactory<>("fld4"));
col_2.setCellFactory(p ->
{
TableCell<myclass,myclass> cell = new TableCell<>()
{
@Override
protected void updateItem(myclass item, boolean empty)
{
if (item != null)
{
Label l = new Label();
l.setText("combination display " + item.fld2Property().getValue() + " " + item.fld4Property().getValue());
setGraphic(l);
}
else
{
setGraphic(null);
setText(null);
}
}
};
return cell;
});
final ObservableList<myclass> olpc = FXCollections.observableArrayList();
olpc.addAll(origlist.stream().toList());
TableView1.setItems(olpc);
}
@FXML
public void ongoClick(ActionEvent actionEvent)
{
origlist.get(0).fld2Property().setValue("Z");
origlist.get(0).fld4Property().setValue("ZZZ");
}
@FXML
public void onbreak(ActionEvent actionEvent)
{
System.out.println(origlist.get(0).fld2Property().get());
}
}
要将OP下的评论压缩为答案,实际上有三种方法可以创建一个表列,该列显示的值取决于模型类中的多个其他值
要进行演示,请从标准 Oracle 文档中表示例的变体开始:
表格行模型类:
package org.jamesd.example.compositecell;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
public Person(String firstName, String lastName) {
this.firstName.set(firstName);
this.lastName.set(lastName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public StringProperty lastNameProperty() {
return lastName;
}
}
和应用程序:
package org.jamesd.example.compositecell;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) throws Exception {
TableView<Person> table = new TableView<>();
table.setEditable(true);
TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
firstNameColumn.setCellValueFactory(data -> data.getValue().firstNameProperty());
firstNameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
lastNameColumn.setCellValueFactory(data -> data.getValue().lastNameProperty());
lastNameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
/* TODO:
Create full name column
*/
table.getColumns().add(firstNameColumn);
table.getColumns().add(lastNameColumn);
// table.getColumns().add(fullNameColumn);
ObservableList<Person> data = createPersonList();
populateData(data);
table.setItems(data);
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private ObservableList<Person> createPersonList() {
return FXCollections.observableArrayList();
}
private void populateData(ObservableList<Person> personList) {
personList.addAll(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
);
}
public static void main(String[] args) {
Application.launch(args);
}
}
对于第一个选项,我们可以向模型添加一个新的只读属性,以获取全名:
public class Person {
// existing code...
private final ReadOnlyStringWrapper fullName = new ReadOnlyStringWrapper();
public Person(String firstName, String lastName) {
this.firstName.set(firstName);
this.lastName.set(lastName);
fullName.bind(this.firstName.concat(" ").concat(this.lastName));
}
public ReadOnlyStringProperty fullNameProperty() {
return fullName.getReadOnlyProperty();
}
// existing code
}
然后像往常一样创建全名列:
/*
Create full name column
*/
TableColumn<Person, String> fullNameColumn = new TableColumn<>("Full Name");
fullNameColumn.setCellValueFactory(data -> data.getValue().fullNameProperty());
对于第二个选项,保留
Person
类的原始形式,并使用创建适当绑定的单元格值工厂:
/*
Create full name column
*/
TableColumn<Person, String> fullNameColumn = new TableColumn<>("Full Name");
fullNameColumn.setCellValueFactory(data -> {
Person person = data.getValue();
return person.firstNameProperty().concat(" ").concat(person.lastNameProperty());
});
或者(也许更普遍):
/*
Create full name column
*/
TableColumn<Person, String> fullNameColumn = new TableColumn<>("Full Name");
fullNameColumn.setCellValueFactory(data -> {
Person person = data.getValue();
return Bindings.createStringBinding(
() -> person.firstNameProperty().get() + " " + person.lastNameProperty().get(),
person.firstNameProperty(),
person.lastNameProperty()
);
});
对于选项 3,再次使用原始
Person
类,并将复合列创建为:
/*
Create full name column
*/
TableColumn<Person, Person> fullNameColumn = new TableColumn<>("Full Name");
fullNameColumn.setCellValueFactory(data -> new SimpleObjectProperty<>(data.getValue()));
fullNameColumn.setCellFactory(tc -> new TableCell<>() {
// not sure you need a label here, but to match OP:
private final Label label = new Label();
@Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (empty || person == null) {
label.textProperty().unbind();
setGraphic(null);
} else {
label.textProperty().bind(
person.firstNameProperty().concat(" ").concat(person.lastNameProperty())
);
setGraphic(label);
}
}
});
这里的要点是,当各个属性发生变化时,不会调用
updateItem()
方法。要强制更新标签的文本(您可以直接使用单元格的 textProperty()
执行相同操作),您需要将其显式绑定到其他属性。