Reputation: 461
I'm programming a little vocabulary trainer for my wife using JavaFX. Each word stores it's own statistic values like how often she encountered the word and how often she provided a correct answer. I've set up a TableView for that, but after a training session the stats won't update. The word objects themselves have the right values but they're just not shown in the TableView. Usually the TableView always displays the current values of on object due to the nature of ObservableList, right? Not in this case though.
This is my word-class:
public class Vocable
{
private String german;
private String english;
private int givenAnswers, correctAnswers, skippedAnswers;
public Vocable(String ger, String eng)
{
german = ger;
english = eng;
givenAnswers = 0;
correctAnswers = 0;
skippedAnswers = 0;
}
public String getGerman()
{
return german;
}
public String getEnglish()
{
return english;
}
public int getGivenAnswers()
{
return givenAnswers;
}
public void increaseGivenAnswers()
{
this.givenAnswers++;;
}
public int getCorrectAnswers()
{
return correctAnswers;
}
public void increaseCorrectAnswers()
{
this.correctAnswers++;
}
public int getSkippedAnswers()
{
return skippedAnswers;
}
public void increaseSkippedAnswers()
{
this.skippedAnswers++;
}
}
And this is my View-class:
public class OverviewView extends VBox
{
private OverviewPresenter presenter;
private TableView<Vocable> overviewTable;
private TableColumn<Vocable, String> german;
private TableColumn<Vocable, String> english;
private TableColumn<Vocable, Number> given;
private TableColumn<Vocable, Number> correct;
private TableColumn<Vocable, Number> skipped;
private Button delete, back, showStats, add;
public OverviewView()
{
initView();
}
private void initView()
{
BorderPane pane = new BorderPane();
overviewTable = new TableView<>();
german = new TableColumn<>("German");
english = new TableColumn<>("English");
given = new TableColumn<>("given");
correct = new TableColumn<>("correct");
skipped = new TableColumn<>("skipped");
german.setResizable(true);
english.setResizable(true);
given.setResizable(true);
correct.setResizable(true);
skipped.setResizable(true);
delete = new Button("Delete");
delete.setOnAction(e -> presenter.deleteVocable(overviewTable.getSelectionModel().getSelectedItem()));
back = new Button("Back");
back.setOnAction(e -> presenter.showMainView());
showStats = new Button("Statistics");
showStats.setOnAction(e -> presenter.showStatistics());
add = new Button("Add Vocbale");
add.setOnAction(e -> presenter.showAddDialog());
overviewTable.getColumns().add(german);
overviewTable.getColumns().add(english);
overviewTable.getColumns().add(given);
overviewTable.getColumns().add(correct);
overviewTable.getColumns().add(skipped);
german.prefWidthProperty().bind(overviewTable.widthProperty().multiply(0.35));
english.prefWidthProperty().bind(overviewTable.widthProperty().multiply(0.35));
given.prefWidthProperty().bind(overviewTable.widthProperty().multiply(0.10));
correct.prefWidthProperty().bind(overviewTable.widthProperty().multiply(0.10));
skipped.prefWidthProperty().bind(overviewTable.widthProperty().multiply(0.10));
pane.setCenter(overviewTable);
HBox box = new HBox(50);
box.getChildren().addAll(back, delete, add, showStats);
pane.setBottom(box);
BorderPane.setMargin(box, Constants.DEFAULT_BP_MARGINS);
pane.setPadding(Constants.DEFAULT_PADDING);
getChildren().add(pane);
}
public void setTableContent(ObservableList<Vocable> list)
{
overviewTable.setItems(list);
german.setCellValueFactory(item -> new SimpleStringProperty(item.getValue().getGerman()));
english.setCellValueFactory(item -> new SimpleStringProperty(item.getValue().getEnglish()));
given.setCellValueFactory(item -> new SimpleIntegerProperty(item.getValue().getGivenAnswers()));
correct.setCellValueFactory(item -> new SimpleIntegerProperty(item.getValue().getCorrectAnswers()));
skipped.setCellValueFactory(item -> new SimpleIntegerProperty(item.getValue().getSkippedAnswers()));
}
}
Any idea on how to fix this?
Upvotes: 1
Views: 1371
Reputation: 209225
Implement the mutable properties in Vocable
using the JavaFX properties pattern. This allows the table cells to observe the appropriate properties and respond if they change. For values that don't change, such as german
and english
, you can still use regular (and final) fields. For values that change but you want to control how they change (e.g. you want an increaseGivenAnswers()
method, but no general setGivenAnswers(int)
method), you can use a ReadOnlyXXXWrapper
and expose a ReadOnlyXXXProperty
. So, e.g.:
public class Vocable
{
private final String german;
private final String english;
private final ReadOnlyIntegerWrapper givenAnswers = new ReadOnlyIntegerWrapper();
private final ReadOnlyIntegerWrapper correctAnswers = new ReadOnlyIntegerWrapper();
private final ReadOnlyIntegerWrapper skippedAnswers = new ReadOnlyIntegerWrapper();
public Vocable(String ger, String eng)
{
german = ger;
english = eng;
givenAnswers.set(0);
correctAnswers.set(0);
skippedAnswers.set(0);
}
public String getGerman()
{
return german;
}
public String getEnglish()
{
return english;
}
public ReadOnlyIntegerProperty givenAnswersProperty() {
return givenAnswers.getReadOnlyProperty() ;
}
public final int getGivenAnswers()
{
return givenAnswersProperty().get();
}
public void increaseGivenAnswers()
{
givenAnswers.set(getGivenAnswers()+1);
}
public ReadOnlyIntegerProperty correctAnswersProperty() {
return correctAnswers.getReadOnlyProperty();
}
public final int getCorrectAnswers()
{
return correctAnswersProperty().get();
}
public void increaseCorrectAnswers()
{
this.correctAnswers.set(getCorrectAnswers()+1);
}
public ReadOnlyIntegerProperty skippedAnswersProperty() {
return skippedAnswers.getReadOnlyProperty();
}
public final int getSkippedAnswers()
{
return skippedAnswersProperty().get();
}
public void increaseSkippedAnswers()
{
this.skippedAnswers.set(getSkippedAnswers()+1);
}
}
For the cells to observe these properties, map the cell value factories to the properties that are defined:
german.setCellValueFactory(item -> new SimpleStringProperty(item.getValue().getGerman()));
english.setCellValueFactory(item -> new SimpleStringProperty(item.getValue().getEnglish()));
given.setCellValueFactory(item -> item.getValue().givenAnswersProperty());
correct.setCellValueFactory(item -> item.getValue().correctAnswersProperty());
skipped.setCellValueFactory(item -> item.getValue().skippedAnswersProperty());
Upvotes: 2