Here's the situation: I have an ObservableSet (because the ID field for my data must be unique) that has a listener. That listener updates an ObservableList. That ObservableList is, in turn, listened to by a TableView. (According to the comments, this is all necessary because ObservableSet can't be used to back a TableView in JavaFX.)
What we're finding, however, is that doing multiple add
operations to the Set don't trigger the refresh of the TableView.
This works fine for:
- Initial list population
- Duplicate set entries (the listener isn't triggered, that's the intended behavior)
However, editing the value, the TableView will only trigger if there's a single add
statement (in the example below, commenting out markStructure.add(new MarkStructureItem(2, 15));
makes it work fine, but then you only have a single value). If there's more than one, the TableView doesn't refresh.
This can be worked around by adding a manual refresh at the end of the Listener class (tblTable.refresh()
). This is actually how it's functioned up until now, but development now requires the removal of that.
Any idea what's going on? Why is the listener on the TableView not getting triggered for subsequent add
s? The data IS being put into the Set and List, that much has been determined; it's just not triggering a refresh.
Code example (not production, but a POC I put together to test and get answers):
public class TestController implements Initializable {
ObservableSet<MarkStructureItem> markStructure = FXCollections.observableSet();
ObservableList<MarkStructureItem> listMarkStructure = FXCollections.observableArrayList();
@FXML
private Pane root;
@FXML
private TableView<MarkStructureItem> tblTable;
@FXML
private TableColumn<MarkStructureItem, Integer> col1;
@FXML
private TableColumn<MarkStructureItem, Integer> col2;
@FXML
private Button btnButton;
private void resetAll() {
markStructure.clear();
markStructure.add(new MarkStructureItem(1, 25));
markStructure.add(new MarkStructureItem(2, 15));
}
@FXML
private void handleButtonAction(ActionEvent event) throws IOException {
Object source = event.getSource();
Button btnSource = (Button) source;
Stage stage = (Stage) root.getScene().getWindow();
switch (btnSource.getId()) {
case "btnButton": {
resetAll();
break;
}
}
}
class MarksUpdater<MarkStructureItem extends configurationeditor.MarkStructureItem> implements SetChangeListener {
@Override
public void onChanged(Change change) {
if (change.wasRemoved()) {
listMarkStructure.remove(change.getElementRemoved());
} else if (change.wasAdded()) {
MarkStructureItem newMarks = (MarkStructureItem) change.getElementAdded();
listMarkStructure.add(newMarks);
}
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
markStructure.addListener(new MarksUpdater<MarkStructureItem>());
col1.setCellValueFactory(
new PropertyValueFactory<MarkStructureItem, Integer>("id")
);
col2.setCellValueFactory(
new PropertyValueFactory<MarkStructureItem, Integer>("marks")
);
col2.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
col2.setOnEditCommit(
new EventHandler<CellEditEvent<MarkStructureItem, Integer>>() {
@Override
public void handle(CellEditEvent<MarkStructureItem, Integer> t) {
((MarkStructureItem) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setMarks(t.getNewValue());
}
}
);
tblTable.setItems(listMarkStructure);
resetAll();
}
}
markStructure class:
public class MarkStructureItem {
final SimpleIntegerProperty marks;
final SimpleIntegerProperty id;
@Override
public int hashCode() {
return this.getId().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj.getClass() == this.getClass()) {
MarkStructureItem thisObj = (MarkStructureItem) obj;
return thisObj.getId() == this.getId();
}
return false;
}
public MarkStructureItem(Integer finishPosition, Integer marks) {
this.marks = new SimpleIntegerProperty(marks);
this.id = new SimpleIntegerProperty(finishPosition);
}
public Integer getId() {
return id.get();
}
public void setMarks(Integer value) {
marks.set(value);
}
public Integer getMarks() {
return marks.get();
}
public void setId(Integer value) {
id.set(value);
}
}