I'm trying to learn JavafX and hence how to think with properties.
I have a bean which I use to update a TableView, like this:
public class DDTTabController extends DefaultTabController implements Initializable {
....
@FXML
private TableView<DDTTableView> ddtTable;
....
@FXML
private TableColumn<DDTTableView, String> rifColumn;
....
}
and so on. I init my Controller like this:
@Override
public void initialize(URL url, ResourceBundle rb) {
....
rifColumn.setCellValueFactory(cellData -> cellData.getValue().getRifProperty());
....
}
This is the bean I use for the View:
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;
}
}
the 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);
}
....
}
and finally the entry point in which I pass my data to the Controller:
public void setMainApp(AppWindow mainApp, MDIWindow win, MDICanvas can) {
super.setMainApp(mainApp, win, can);
dataManager.getDDT().stream().forEach((ddt) -> {
actualDDT.add(new DDTTableView(ddt));
});
}
And you can see I only use the 2nd contructor (the one with the parameter).
Now the problem is that even if the Rows property is updated in the DDT bean, the Rif property desn't get rebuilt because the ChangeListener does not trigger, and I cannot understand why.
Can anybody please shed some light?
Thanks.
In the
DDTTableView
constructor, you set the value of theddt
property to the value passed as a parameter, and then you add a listener to therowsProperty
of the current value ofddt
:There are three problems that I can see here:
ddt
is mutable (can be set to a new value), and if it is changed, the listener will still be attached to therowsProperty
of the original value, not the current value.ddt.setRows(/* another entire list of DDTItems */)
, but will not respond to changes to the current list. So it won't respond toddt.getRows().add(/* some DDTItem */);
, etc.DDTTableView
constructor does not add the listener (only the constructor taking a parameter does).For the first problem, you need to observe your
ddt
property and move the listener if its value changes.For the second problem, I recommend not making the list mutable, but simply modifiable. Instead of
ddt.setRows(someOtherList)
(if you ever need it), you can always doddt.getRows().setAll(someOtherList)
, which has essentially the same effect1. Then just register aListChangeListener
will the list.The third problem is easily fixed by moving the code that attaches the listener to the default constructor (which is invoked by the other constructor).
You should also fix your method names so they match the JavaFX properties pattern.
I.e.:
and now
1 If you really need
setRows(...)
functionality, you can use aListProperty<DDTItem>
instead of aObjectProperty<ObservableList<DDTItem>>
. This will notifyListChangeListener
s if either the underlying list is changed viasetRows(...)
or if the current list is modified viagetRows().add(...)
etc. The use cases for this are very rare, though, and usually it is enough just to have an immutable modifiable list as in the code above.