1, 最简单粗暴,但也最不合理的做法
在数据对象更新后,调用 table.refresh() 方法。
这样做,表格实际上是刷新了,因为绑定的list里的对象确实发生了变化。
* 但是,这样并不符合数据绑定的工作方式,实际上调用refresh()的结果是重新渲染表格,可理解为表格初始化为空白后,重新加载了,当然实际fx不是这么做,但refresh()意味着实际是UI层重新初始化。
* 并不是数据绑定的监听起了作用。原因(痛点):table 绑定 list ,Oracle 只实现了增加、删除2个动作的监听,即是说,list 中单个元素本身属性值的修改,并不会被数据绑定所监听。
因为以上原因,出现的现象就是:
当 list 中元素有增、删时候, table 能够实时刷新。
当 list 中元素属性变化时候, table 不能实时刷新。(因为此时修改事件并不受监听,list 不认为自己有变化,随之 table 也就不会刷新)。
—- 第1种情况,致使 list 监听不到变化的前提是,修改的属性为JavaBean普通属性类型(如String,Integer) ,对 table cell 定义如:
tc_status.setCellValueFactory(new PropertyValueFactory<Account,String>("status"));
2,因为JavaBean普通属性,即使通过
new PropertyValueFactory<Account,String>("name")
这样的包装,变成可以在 table 中显示,但实际上偏离了 JavaFx 的设计初衷。
与 JavaFx UI 层进行交互的类属性,应当定义为对应的 xxxProperty 包装类 (如 StringProperty , IntegerProperty 等 )。
然后,在对 table cellValueFactory 定义时,就可以直接返回 xxxProperty 对象 (因为 StringProperty 本身就是 ObservableValue 的子类)
代码实例:
// 定义为 final 好像是一种规范做法
private final StringProperty status = new SimpleStringProperty();
// * 特别说明: xxxProperty 方法名,是 fx 的规范,只要属性名加上 Peoperty() 作为方法名,fx 就能自动监听该属性的变化!
public StringProperty statusProperty() {
return status;
}
// 原有的 set 方法,并不受字段类型变化而改变,仍然接受同样的参数,只是方法体需要修改一下
public void setStatus(String status) {
this.status.set(status);
}
// 原有的 get 方法,并不受字段类型变化而改变,仍然返回同样的类型,只是方法体需要修改一下
public String getStatus(){
return status.get();
}
....
// Controller 中定义
// 这样的写法就行了!当该属性值变化时,fx 绑定能立即捕捉到,并且刷新UI
tc_status.setCellValueFactory(new PropertyValueFactory("status"));
经过这样对原来JavaBean属性的改造,当属性发生变化时,list 能够捕捉到,因此 table 上对应的值也能实时刷新。(这才是数据绑定,而不是第1种做法中对table UI层的强制刷新)
要注意,如果是 IntegerProperty , DoubleProperty 数字类型的,应定义为 Number ,而不是 Interger,如下所示,对应的 setCellValueFactory 方法对应参数类型也应为 Number。(* 非必须定义,可以不定义)
TableColumn<Account,Number> tc_num4xxx;
前面定义 setValueFactory 的方法,在 JDK8 中可以这样写:
tc_num4xxx.setCellValueFactory(param->param.getValue().num4xxx());
3, 高级做法:
table 上每个单元格,实际上不只可以容纳String、Integer 等类型,还可以容纳各种FX UI控件,
比如可以将单元格填充为 Label , CheckBox , ProgressBar
示例 (把 ProgressBar 塞进去)
tc_progress.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<Account, ProgressBar>, ObservableValue<ProgressBar>>() {
@Override
public ObservableValue<ProgressBar> call(
TableColumn.CellDataFeatures<Account, ProgressBar> param) {
// 取出 table 上绑定的对应对象
Account account = param.getValue();
// 制造 ProgressBar UI 实例
ProgressBar progressbar = new ProgressBar();
// 绑定 progress这个property属性到UI上
progressbar.progressProperty().bind(account.progress());
// 返回 ProgressBar 类型对象
return new SimpleObjectProperty<ProgressBar>(progressbar);
}
});
如果要塞一个 Label 进去,同理参数类型设为 Label , 然后给想要单元格能够监听的属性 添加onChange监听,这样当属性变化时, Label 的 text 能够被更新,也就实现了刷新的目的。
tc_status.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Account,Label>, ObservableValue<Label>>(){
@Override
public ObservableValue<Label> call(CellDataFeatures<Account, Label> param) {
// TODO 定义 Label
final Label label = new Label(param.getValue().getStatus());
try {
label.setText(param.getValue().getStatus());
// 添加对属性值的监听事件,当值变化时,更新 Label 的 text 属性
param.getValue().status().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
// TODO
Platform.runLater(()->
label.setText("-->"+newValue)
);
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new SimpleObjectProperty<Label>(label);
}
/////////////////////////////////////////////////////
});