Reputation: 1876
i have this TableView of tasks that has a column of checkboxes. i've managed to extract the column values using stream().map(). i want to count the true values in that list.
ObservableList<BooleanProperty> list = FXCollections.observableArrayList(tasksTable.getItems().stream().map(t -> t.completedProperty()).collect(Collectors.toList()));
and another integer property
SimpleIntegerProperty count = new SimpleIntegerProperty(0);
i want this count value to be updated to the total number of true values in the list. how can i do this. i tried binding it like this
IntegerBinding count = Bindings.createIntegerBinding(() -> {
return (int)list.stream().filter(done -> done.get()).count();
}, list);
and added a listener to monitor for changes.
count.addListener((o, oldValue, newValue) -> {
System.out.println('value changed ' + newValue);
});
but its not working... nothing gets printed when i click on any checkbox...
Upvotes: 0
Views: 902
Reputation: 82491
You binding doesn't update, if the BooleanProperty
s in the list change, only when the list itself is modified. You can specify conditions that trigger update changes using the correct observableArrayList
method.
// update triggers every time a BooleanProperty in the list is changed
ObservableList<BooleanProperty> list = FXCollections.observableArrayList((Callback<BooleanProperty, Observable[]>) e -> new Observable[] {e});
list.add(new SimpleBooleanProperty(false));
IntegerBinding trueNum = Bindings.createIntegerBinding(() -> (int) list.stream().map(BooleanProperty::get).filter(b -> b).count(), list);
trueNum.addListener((a, b, c) -> {
System.out.println(c);
});
System.out.println(trueNum.get());
list.get(0).set(true);
list.add(new SimpleBooleanProperty(false));
list.add(new SimpleBooleanProperty(true));
list.get(2).set(false);
The output is
0
1
2
1
This has the drawback of reevaluating the whole list every time one of the properties changes or the list is modified. You can do this more efficiently, if you use a ListChangeListener
and register change listeners to all of the properties contained in the list:
ObservableList<BooleanProperty> list = FXCollections.observableArrayList();
...
SimpleIntegerProperty count = new SimpleIntegerProperty();
ChangeListener<Boolean> changeListener = (observable, oldValue, newValue) -> {
// increment or decrement if value was changed to true or false respecively
count.set(count.get()+ (newValue ? 1 : -1));
};
// get initial value and register change listeners to elements of list
int currentCount = 0;
for (BooleanProperty p : list) {
if (p.get()) {
currentCount++;
}
p.addListener(changeListener);
}
count.set(currentCount);
list.addListener((ListChangeListener.Change<? extends BooleanProperty> c) -> {
int modification = 0;
while (c.next()) {
// remove listeners from all properties removed in change
// and calculate update of value
for (BooleanProperty p : c.getRemoved()) {
p.removeListener(changeListener);
if (p.getValue()) {
modification--;
}
}
// add listeners from all properties added in change
// and calculate update of value
for (BooleanProperty p : c.getAddedSubList()) {
p.addListener(changeListener);
if (p.getValue()) {
modification++;
}
}
}
// update count
count.set(count.get()+modification);
});
Upvotes: 2
Reputation: 895
Your lambda is called only once. You specify dependency list
but uses tasksTable.getItems()
In lambda-extractor you should use only values you specify as dependencies.
Also take a look at EasyBind library.
Upvotes: 0