理论上,您可以通过使用提取器创建列表来监视可观察列表中的项目的更改。这个想法是,对于 TableView,您指定一个 Callback,它为列表中的每个对象提供一组 Observables。列表将观察可观察对象,并且当现有项目对指定属性进行更改时,将通知任何在列表中注册的 ListChangeListeners(通过 wasUpdated() 返回 true 的更改)。
然而,这似乎只适用于 Java 8;JavaFX 2.2 的某个地方可能存在一个错误。
这是一个示例,基于通常的 TableView 示例。
import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableUpdatePropertyExample extends Application {
@Override
public void start(Stage primaryStage) {
final TableView<Person> table = new TableView<>();
table.setEditable(true);
final TableColumn<Person, String> firstNameCol = createTableColumn("First Name");
final TableColumn<Person, String> lastNameCol = createTableColumn("Last Name");
final TableColumn<Person, String> emailCol = createTableColumn("Email");
table.getColumns().addAll(Arrays.asList(firstNameCol, lastNameCol, emailCol));
final List<Person> data = Arrays.asList(
new Person("Jacob", "Smith", "jacob.smith@example.com"),
new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
new Person("Ethan", "Williams", "ethan.williams@example.com"),
new Person("Emma", "Jones", "emma.jones@example.com"),
new Person("Michael", "Brown", "michael.brown@example.com")
);
table.setItems(FXCollections.observableList(data, new Callback<Person, Observable[]>() {
@Override
public Observable[] call(Person person) {
return new Observable[] {person.firstNameProperty(), person.lastNameProperty(), person.emailProperty()};
}
}));
table.getItems().addListener(new ListChangeListener<Person>() {
@Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends Person> change) {
while (change.next()) {
if (change.wasUpdated()) {
Person updatedPerson = table.getItems().get(change.getFrom());
System.out.println(updatedPerson+" was updated");
}
}
}
});
final BorderPane root = new BorderPane();
root.setCenter(table);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private TableColumn<Person, String> createTableColumn(String title) {
TableColumn<Person, String> col = new TableColumn<>(title);
col.setCellValueFactory(new PropertyValueFactory<Person, String>(makePropertyName(title)));
col.setCellFactory(TextFieldTableCell.<Person>forTableColumn());
return col ;
}
private String makePropertyName(String text) {
boolean first = true ;
StringBuilder prop = new StringBuilder();
for (String word : text.split("\\s")) {
if (first) {
prop.append(word.toLowerCase());
} else {
prop.append(Character.toUpperCase(word.charAt(0)));
if (word.length() > 1) {
prop.append(word.substring(1));
}
}
first=false ;
}
return prop.toString();
}
public static class Person {
private final StringProperty firstName ;
private final StringProperty lastName ;
private final StringProperty email ;
public Person(String firstName, String lastName, String email) {
this.firstName = new SimpleStringProperty(this, "firstName", firstName);
this.lastName = new SimpleStringProperty(this, "lastName", lastName);
this.email = new SimpleStringProperty(this, "email", email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName ;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName ;
}
public String getEmail() {
return email.get();
}
public void setEmail(String email) {
this.email.set(email);
}
public StringProperty emailProperty() {
return email ;
}
@Override
public String toString() {
return getFirstName() + " " + getLastName();
}
}
public static void main(String[] args) {
launch(args);
}
}
对于验证,您可以考虑覆盖模型类属性中的 set 和 setValue 方法。我还没有尝试过,我怀疑您可能需要弄乱 TableCell 才能使其正常工作,但是类似于:
this.email = new StringPropertyBase(email) {
final Pattern pattern = Pattern.compile("[a-zA-Z_0-9]+@[a-zA-Z0-9.]+");
@Override
public String getName() {
return "email";
}
@Override
public Object getBean() {
return Person.this;
}
@Override
public void set(String email) {
if (pattern.matcher(email).matches()) {
super.set(email);
}
}
@Override
public void setValue(String email) {
if (pattern.matcher(email).matches()) {
super.setValue(email);
}
}
};
代替上面 Person 类中的一个衬垫。
更新:要在 JavaFX 2.2 中进行这项工作,您基本上可以推出自己的“提取器”版本。这有点工作,但还不错。像下面这样的东西似乎工作,例如:
final List<Person> data = Arrays.asList(
new Person("Jacob", "Smith", "jacob.smith@example.com"),
new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
new Person("Ethan", "Williams", "ethan.williams@example.com"),
new Person("Emma", "Jones", "emma.jones@example.com"),
new Person("Michael", "Brown", "michael.brown@example.com")
);
final ChangeListener<String> firstNameListener = new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> obs,
String oldFirstName, String newFirstName) {
Person person = (Person)((StringProperty)obs).getBean();
System.out.println("First name for "+person+" changed from "+oldFirstName+" to "+newFirstName);
}
};
final ChangeListener<String> lastNameListener = new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> obs,
String oldLastName, String newLastName) {
Person person = (Person)((StringProperty)obs).getBean();
System.out.println("Last name for "+person+" changed from "+oldLastName+" to "+oldLastName);
}
};
final ChangeListener<String> emailListener = new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> obs,
String oldEmail, String newEmail) {
Person person = (Person)((StringProperty)obs).getBean();
System.out.println("Email for "+person+" changed from "+oldEmail+" to "+oldEmail);
}
};
table.getItems().addListener(new ListChangeListener<Person>() {
@Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends Person> change) {
while (change.next()) {
for (Person person : change.getAddedSubList()) {
person.firstNameProperty().addListener(
firstNameListener);
person.lastNameProperty().addListener(lastNameListener);
person.emailProperty().addListener(emailListener);
}
for (Person person : change.getRemoved()) {
person.firstNameProperty().removeListener(
firstNameListener);
person.lastNameProperty().removeListener(
lastNameListener);
person.emailProperty().removeListener(emailListener);
}
}
}
});
table.getItems().addAll(data);