我们正在将JavaFX集成到包含许多“原始”Java bean的大型遗留代码库中,即使用java.beans.PropertyChangeSupport
实现的类型。
JavaFX不支持更新这些样式的bean,只支持初始值,如javafx.scene.control.cell.PropertyValueFactory
中所述如果不存在与此模式匹配的方法,则存在漏洞 支持尝试调用get()或is()(即 是,上面示例中的getFirstName()或isFirstName())。如果一个方法 如果匹配此模式,则此方法返回的值为 包装在ReadOnlyObjectWrapper中并返回到TableCell。 但是,在这种情况下,这意味着TableCell不会 能够观察ObservableValue的变化(如同的情况) 第一种方法)。
将bean升级到属性API不是一种选择,因为它们位于一个单独的代码库中,我们不希望添加JavaFX依赖项,因为它仍然被旧版Java 6项目使用。
我的问题是,如何在更改属性时更新TableView,而无需在表中的所有单个bean上添加/删除侦听器。
我正在考虑创建我自己的PropertyValueFactory版本,它支持这个,但我想知道是否还有其他可能的解决方案。
我已经举了两个例子来说明这一点。
public class OldBeanTableView extends Application {
public class OldBean {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public static final String PROPERTY_NAME_FOO = "foo";
private int foo = 99;
public int getFoo() {
return foo;
}
public void setFoo(int foo) {
int oldValue = this.foo;
this.foo = foo;
pcs.firePropertyChange(PROPERTY_NAME_FOO, oldValue, foo);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
ObservableList<OldBean> beans = FXCollections.observableArrayList();
beans.add(new OldBean());
TableView<OldBean> tableView = new TableView<>();
TableColumn<OldBean, Integer> column = new TableColumn<OldBeanTableView.OldBean, Integer>();
tableView.getColumns().add(column);
column.setCellValueFactory(new PropertyValueFactory<>("foo"));
tableView.setItems(beans);
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> beans.get(0).setFoo(beans.get(0).getFoo() + 1), 0,
1, TimeUnit.SECONDS);
}
}
public class NewBeanTableView extends Application {
public class NewBean {
private IntegerProperty fooProperty = new SimpleIntegerProperty(0);
public int getFoo() {
return fooProperty.get();
}
public void setFoo(int foo) {
fooProperty.set(foo);
}
public IntegerProperty fooProperty() {
return fooProperty;
}
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
ObservableList<NewBean> beans = FXCollections.observableArrayList();
beans.add(new NewBean());
TableView<NewBean> tableView = new TableView<>();
TableColumn<NewBean, Integer> column = new TableColumn<NewBeanTableView.NewBean, Integer>();
tableView.getColumns().add(column);
column.setCellValueFactory(new PropertyValueFactory<>("foo"));
tableView.setItems(beans);
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> beans.get(0).setFoo(beans.get(0).getFoo() + 1), 0,
1, TimeUnit.SECONDS);
}
}
答案 0 :(得分:2)
将JavaBeanProperty用作valueFactory的一个非常快速的示例:
Callback<CellDataFeatures<OldBean, Integer>, ObservableValue<Integer>> valueFactory = cdf -> {
OldBean bean = cdf.getValue();
JavaBeanObjectProperty<Integer> wrappee;
try {
wrappee = JavaBeanObjectPropertyBuilder.create()
.name("foo").bean(bean).build();
return wrappee;
} catch (Exception e) {
e.printStackTrace();
}
return null;
};
column.setCellValueFactory(valueFactory);
请注意,bean必须有方法add / removePropertyChangeListeners(无论如何你的真正的bean都会:-)才能工作。
答案 1 :(得分:2)
推断kleopatra对通用解决方案的回答。
public class LegacyValueFactory<T, F> implements Callback<CellDataFeatures<T, F>, ObservableValue<F>> {
private String propertyName;
public LegacyValueFactory(String propertyName) {
this.propertyName = propertyName;
}
@Override
public ObservableValue<F> call(CellDataFeatures<T, F> param) {
try {
return JavaBeanObjectPropertyBuilder.create().name(propertyName).bean(param.getValue()).build();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
}
用法
column.setCellValueFactory(new LegacyValueFactory<OldBean, Integer>("foo"));