ChangeListener 未在 Javafx 中触发
ChangeListener not triggering in Javafx
我正在努力学习 JavafX 以及如何思考属性。
我有一个用于更新 TableView 的 bean,如下所示:
public class DDTTabController extends DefaultTabController implements Initializable {
....
@FXML
private TableView<DDTTableView> ddtTable;
....
@FXML
private TableColumn<DDTTableView, String> rifColumn;
....
}
等等。我这样初始化我的控制器:
@Override
public void initialize(URL url, ResourceBundle rb) {
....
rifColumn.setCellValueFactory(cellData -> cellData.getValue().getRifProperty());
....
}
这是我用于视图的 bean:
private class DDTTableView {
private ObjectProperty<DDT> ddt;
private ObjectProperty<Contact> contact;
private StringProperty rif;
public DDTTableView() {
this.ddt = new SimpleObjectProperty<>();
this.contact = new SimpleObjectProperty<>();
this.rif = new SimpleStringProperty("");
}
public DDTTableView(DDT o) {
this();
this.setDdt(o);
this.setContact(dataManager.getContactForCodeType(o.getAnaTipe(), o.getAnaCode().trim()));
this.ddt.get().getRowsProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println("bip!");
rif.set(...... buildString ......);
}
});
}
public StringProperty getRifProperty() {
return this.rif;
}
public String getRif() {
return this.rif.get();
}
public void setRif(String r) {
this.rif.set(r);
}
public ObjectProperty<DDT> getDdtProperty() {
return ddt;
}
public DDT getDdt() {
return ddt.get();
}
public void setDdt(DDT ddt) {
this.ddt.set(ddt);
}
public ObjectProperty<Contact> getContactProperty() {
return contact;
}
public Contact getContact() {
return contact.get();
}
public void setContact(Contact contact) {
this.contact.set(contact);
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.ddt);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DDTTableView other = (DDTTableView) obj;
if (!Objects.equals(this.ddt, other.ddt)) {
return false;
}
return true;
}
}
DDT bean:
public class DDT {
....
private ObjectProperty<ObservableList<DDTItem>> rows;
....
}
public DDT() {
....
this.rows = new SimpleObjectProperty<>(FXCollections.observableArrayList());
....
}
public ObjectProperty<ObservableList<DDTItem>> getRowsProperty() {
return rows;
}
public ObservableList<DDTItem> getRows() {
return rows.get();
}
public void setRighe(ObservableList<DDTItem> r) {
this.rows.set(r);
}
....
}
最后是我将数据传递给控制器的入口点:
public void setMainApp(AppWindow mainApp, MDIWindow win, MDICanvas can) {
super.setMainApp(mainApp, win, can);
dataManager.getDDT().stream().forEach((ddt) -> {
actualDDT.add(new DDTTableView(ddt));
});
}
你可以看到我只使用了第二个构造器(带参数的那个)。
现在的问题是,即使在 DDT bean 中更新了 Rows 属性,Rif 属性 也不会重建,因为 ChangeListener 不会触发,我不明白为什么.
任何人都可以解释一下吗?
谢谢。
在DDTTableView
构造函数中,将ddt
属性的值设置为作为参数传递的值,然后向[=15=添加监听器]的当前值 ddt
:
this.ddt.get().getRowsProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println("bip!");
rif.set(...... buildString ......);
}
});
我在这里看到了三个问题:
ddt
是可变的(可以设置为新值),如果它被改变,监听器仍然会附加到原始[的rowsProperty
值,不是当前值。
- 您只监听整个行列表的变化。 IE。您的听众将响应
ddt.setRows(/* another entire list of DDTItems */)
,但不会响应对当前列表的更改。所以它不会响应ddt.getRows().add(/* some DDTItem */);
,等等
- 您的默认
DDTTableView
构造函数不添加侦听器(只有带参数的构造函数才会添加)。
对于第一个问题,您需要观察您的 ddt
属性 并在其值发生变化时移动侦听器。
对于第二个问题,我建议不要使列表可变,而只是可修改。除了 ddt.setRows(someOtherList)
(如果你需要它),你总是可以做 ddt.getRows().setAll(someOtherList)
,它具有基本相同的效果 1。然后只需注册一个 ListChangeListener
列表即可。
通过移动将侦听器附加到默认构造函数(由其他构造函数调用)的代码,可以轻松解决第三个问题。
您还应该修改您的方法名称,使其与 JavaFX properties pattern.
匹配
即:
public class DDT {
//...
private final ObservableList<DDTItem> rows;
// ...
public DDT() {
// ...
this.rows = FXCollections.observableArrayList();
// ...
}
public ObservableList<DDTItem> getRows() {
return rows.get();
}
// ...
}
现在
private class DDTTableView {
private ObjectProperty<DDT> ddt;
private ObjectProperty<Contact> contact;
private StringProperty rif;
public DDTTableView() {
this.ddt = new SimpleObjectProperty<>();
this.contact = new SimpleObjectProperty<>();
this.rif = new SimpleStringProperty("");
this.setContact(dataManager.getContactForCodeType(o.getAnaTipe(), o.getAnaCode().trim()));
ListChangeListener<DDTItem> rowsListener = (ListChangeListener.Change<? extends DDTItem> change) -> {
rif.set(/* buildString */);
};
this.ddt.addListener((obs, oldDdt, newDdt) -> {
if (oldDdt != null) {
oldDdt.getRows().removeListener(rowsListener);
}
if (newDdt != null) {
newDdt.getRows().addListener(rowsListener);
}
});
}
public DDTTableView(DDT o) {
this();
this.setDdt(o);
}
public StringProperty rifProperty() {
return this.rif;
}
public String getRif() {
return this.rif.get();
}
public void setRif(String r) {
this.rif.set(r);
}
public ObjectProperty<DDT> ddtProperty() {
return ddt;
}
public DDT getDdt() {
return ddt.get();
}
public void setDdt(DDT ddt) {
this.ddt.set(ddt);
}
public ObjectProperty<Contact> contactProperty() {
return contact;
}
public Contact getContact() {
return contact.get();
}
public void setContact(Contact contact) {
this.contact.set(contact);
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.ddt);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DDTTableView other = (DDTTableView) obj;
if (!Objects.equals(this.ddt, other.ddt)) {
return false;
}
return true;
}
}
1 如果您 确实 需要 setRows(...)
功能,您可以使用 ListProperty<DDTItem>
而不是 ObjectProperty<ObservableList<DDTItem>>
。如果通过 setRows(...)
更改了基础列表,或者如果通过 getRows().add(...)
等修改了当前列表,这将通知 ListChangeListener
s。不过,这种用例非常罕见,而且通常就像上面的代码一样,只要有一个不可变的可修改列表就足够了。
我正在努力学习 JavafX 以及如何思考属性。
我有一个用于更新 TableView 的 bean,如下所示:
public class DDTTabController extends DefaultTabController implements Initializable {
....
@FXML
private TableView<DDTTableView> ddtTable;
....
@FXML
private TableColumn<DDTTableView, String> rifColumn;
....
}
等等。我这样初始化我的控制器:
@Override
public void initialize(URL url, ResourceBundle rb) {
....
rifColumn.setCellValueFactory(cellData -> cellData.getValue().getRifProperty());
....
}
这是我用于视图的 bean:
private class DDTTableView {
private ObjectProperty<DDT> ddt;
private ObjectProperty<Contact> contact;
private StringProperty rif;
public DDTTableView() {
this.ddt = new SimpleObjectProperty<>();
this.contact = new SimpleObjectProperty<>();
this.rif = new SimpleStringProperty("");
}
public DDTTableView(DDT o) {
this();
this.setDdt(o);
this.setContact(dataManager.getContactForCodeType(o.getAnaTipe(), o.getAnaCode().trim()));
this.ddt.get().getRowsProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println("bip!");
rif.set(...... buildString ......);
}
});
}
public StringProperty getRifProperty() {
return this.rif;
}
public String getRif() {
return this.rif.get();
}
public void setRif(String r) {
this.rif.set(r);
}
public ObjectProperty<DDT> getDdtProperty() {
return ddt;
}
public DDT getDdt() {
return ddt.get();
}
public void setDdt(DDT ddt) {
this.ddt.set(ddt);
}
public ObjectProperty<Contact> getContactProperty() {
return contact;
}
public Contact getContact() {
return contact.get();
}
public void setContact(Contact contact) {
this.contact.set(contact);
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.ddt);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DDTTableView other = (DDTTableView) obj;
if (!Objects.equals(this.ddt, other.ddt)) {
return false;
}
return true;
}
}
DDT bean:
public class DDT {
....
private ObjectProperty<ObservableList<DDTItem>> rows;
....
}
public DDT() {
....
this.rows = new SimpleObjectProperty<>(FXCollections.observableArrayList());
....
}
public ObjectProperty<ObservableList<DDTItem>> getRowsProperty() {
return rows;
}
public ObservableList<DDTItem> getRows() {
return rows.get();
}
public void setRighe(ObservableList<DDTItem> r) {
this.rows.set(r);
}
....
}
最后是我将数据传递给控制器的入口点:
public void setMainApp(AppWindow mainApp, MDIWindow win, MDICanvas can) {
super.setMainApp(mainApp, win, can);
dataManager.getDDT().stream().forEach((ddt) -> {
actualDDT.add(new DDTTableView(ddt));
});
}
你可以看到我只使用了第二个构造器(带参数的那个)。
现在的问题是,即使在 DDT bean 中更新了 Rows 属性,Rif 属性 也不会重建,因为 ChangeListener 不会触发,我不明白为什么.
任何人都可以解释一下吗?
谢谢。
在DDTTableView
构造函数中,将ddt
属性的值设置为作为参数传递的值,然后向[=15=添加监听器]的当前值 ddt
:
this.ddt.get().getRowsProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println("bip!");
rif.set(...... buildString ......);
}
});
我在这里看到了三个问题:
ddt
是可变的(可以设置为新值),如果它被改变,监听器仍然会附加到原始[的rowsProperty
值,不是当前值。- 您只监听整个行列表的变化。 IE。您的听众将响应
ddt.setRows(/* another entire list of DDTItems */)
,但不会响应对当前列表的更改。所以它不会响应ddt.getRows().add(/* some DDTItem */);
,等等 - 您的默认
DDTTableView
构造函数不添加侦听器(只有带参数的构造函数才会添加)。
对于第一个问题,您需要观察您的 ddt
属性 并在其值发生变化时移动侦听器。
对于第二个问题,我建议不要使列表可变,而只是可修改。除了 ddt.setRows(someOtherList)
(如果你需要它),你总是可以做 ddt.getRows().setAll(someOtherList)
,它具有基本相同的效果 1。然后只需注册一个 ListChangeListener
列表即可。
通过移动将侦听器附加到默认构造函数(由其他构造函数调用)的代码,可以轻松解决第三个问题。
您还应该修改您的方法名称,使其与 JavaFX properties pattern.
匹配即:
public class DDT {
//...
private final ObservableList<DDTItem> rows;
// ...
public DDT() {
// ...
this.rows = FXCollections.observableArrayList();
// ...
}
public ObservableList<DDTItem> getRows() {
return rows.get();
}
// ...
}
现在
private class DDTTableView {
private ObjectProperty<DDT> ddt;
private ObjectProperty<Contact> contact;
private StringProperty rif;
public DDTTableView() {
this.ddt = new SimpleObjectProperty<>();
this.contact = new SimpleObjectProperty<>();
this.rif = new SimpleStringProperty("");
this.setContact(dataManager.getContactForCodeType(o.getAnaTipe(), o.getAnaCode().trim()));
ListChangeListener<DDTItem> rowsListener = (ListChangeListener.Change<? extends DDTItem> change) -> {
rif.set(/* buildString */);
};
this.ddt.addListener((obs, oldDdt, newDdt) -> {
if (oldDdt != null) {
oldDdt.getRows().removeListener(rowsListener);
}
if (newDdt != null) {
newDdt.getRows().addListener(rowsListener);
}
});
}
public DDTTableView(DDT o) {
this();
this.setDdt(o);
}
public StringProperty rifProperty() {
return this.rif;
}
public String getRif() {
return this.rif.get();
}
public void setRif(String r) {
this.rif.set(r);
}
public ObjectProperty<DDT> ddtProperty() {
return ddt;
}
public DDT getDdt() {
return ddt.get();
}
public void setDdt(DDT ddt) {
this.ddt.set(ddt);
}
public ObjectProperty<Contact> contactProperty() {
return contact;
}
public Contact getContact() {
return contact.get();
}
public void setContact(Contact contact) {
this.contact.set(contact);
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.ddt);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DDTTableView other = (DDTTableView) obj;
if (!Objects.equals(this.ddt, other.ddt)) {
return false;
}
return true;
}
}
1 如果您 确实 需要 setRows(...)
功能,您可以使用 ListProperty<DDTItem>
而不是 ObjectProperty<ObservableList<DDTItem>>
。如果通过 setRows(...)
更改了基础列表,或者如果通过 getRows().add(...)
等修改了当前列表,这将通知 ListChangeListener
s。不过,这种用例非常罕见,而且通常就像上面的代码一样,只要有一个不可变的可修改列表就足够了。