My question arose when implementing some sorts of graph visualization with JavaFX. There are 2 classes called Vertex and Edge, with each edge connecting 2 (possibly the same) vertices. Every vertex that has self-loops (edges having the same start and end vertices) stores a DoubleProperty for the preferred angle of its self-loops. This angle is computed from the the positions of this vertex and all its neighbors. However, as the graph is constructed dynamically, neighbors of a vertex may change, resulting in a dynamic list of dependencies, so I have to modify the dependendies of the DoubleBinding to which the angle is bound.
However, the getDependencies method in the DoubleBinding created by Bindings.createDoubleBinding only returns an immutable copy:
@Override
public ObservableList<?> getDependencies() {
return ((dependencies == null) || (dependencies.length == 0))?
FXCollections.emptyObservableList()
: (dependencies.length == 1)?
FXCollections.singletonObservableList(dependencies[0])
: new ImmutableObservableList<Observable>(dependencies);
}
And although the DoubleBinding class has an bind method that seems to satisfy my need, it is protected:
protected final void bind(Observable... dependencies) {
if ((dependencies != null) && (dependencies.length > 0)) {
if (observer == null) {
observer = new BindingHelperObserver(this);
}
for (final Observable dep : dependencies) {
dep.addListener(observer);
}
}
}
So is there any way that I can modify the dependencies at any time without defining my own DoubleBinding, or can I solve my problem without touching the dependencies?
You can bind your DoubleProperty to an ObservableList containing the neighbor nodes. This way the binding will be invalidated if anything is added to or removed from the list. Here's a very quick demo of the idea:
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class BindingDemo {
public static void main(String[] args) {
ObservableList<GraphNode> neighbors = FXCollections.observableArrayList();
IntegerProperty total = new SimpleIntegerProperty();
total.bind(Bindings.createIntegerBinding(
() -> sum(neighbors),
neighbors
));
total.addListener((obs, oldTotal, newTotal) ->
System.out.println("Total = "+newTotal));
for (int i = 1 ; i <= 5 ; i++) {
System.out.println("Adding node with value "+i+":");
neighbors.add(new GraphNode(i));
}
}
private static int sum(ObservableList<GraphNode> nodes) {
int total = 0 ;
for (GraphNode node : nodes) {
total += node.value();
}
return total ;
}
public static record GraphNode(int value){}
}
Output:
Adding node with value 1:
Total = 1
Adding node with value 2:
Total = 3
Adding node with value 3:
Total = 6
Adding node with value 4:
Total = 10
Adding node with value 5:
Total = 15
Technically, this doesn't really avoid your "without defining my own binding" requirement, but the Bindings.createXXXBinding(...) API makes it pretty clean to do so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With