[ListView] `b.bind(a)` not behaviorally equivalent to `a.addListener(o -> b.set(a.get()))`
Tomas Mikula
tomas.mikula at gmail.com
Fri Dec 13 11:15:22 PST 2013
I just came across a strange case when these two are not equivalent.
Maybe that is no surprise to you, but it was to me. The case I
stumbled upon most likely has to do with ListView internals.
Substitute
a := ListView.widthProperty()
b := ListCell.prefWidthProperty()
and the code that demonstrates the difference:
public class Test extends Application {
private static class MyCell extends ListCell<String> {
public MyCell(ListView<String> lv) {
setMaxWidth(Region.USE_PREF_SIZE);
// !!! comment out exactly one of the following
// !!! two lines to demonstrate the difference
prefWidthProperty().bind(lv.widthProperty());
lv.widthProperty().addListener(o -> setPrefWidth(lv.getWidth()));
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : new TextFlow(new Text(item)));
}
}
@Override
public void start(Stage stage) {
ListView<String> listView = new ListView<>();
listView.setCellFactory(lv -> new MyCell(lv));
listView.getItems().add("This is a very long line that needs
to be wrapped");
StackPane stack = new StackPane();
stack.getChildren().add(listView);
Scene scene = new Scene(stack, 200, 100);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When I run the "bind" version, the text shows up wrapped.
When I run the "addListener" version, the text shows up in one long line.
Do you think this is a bug in ListView, or is there a reasonable explanation?
Just for the record, I consider the "wrapping" behavior to be the correct one.
My *speculation* about the possible cause follows:
The only case I could come up with when
A) b.bind(a)
and
B) a.addListener(o -> b.set(a.get()))
are effectively different is when both of these conditions hold:
1) there is an invalidation listener set on b that causes b to become
valid (e.g. calls b.get()); and
2) a is invalidated twice in a row, but recomputes to the same value each time.
Now the scenarios for A) and B) differ:
Scenario A (b.bind(a)):
- a is invalidated for the first time
- b's invalidation listener is called for the first time
- b is now valid
- a is invalidated for the second time
- b's invalidation listener is called for the *SECOND* time
Scenario B (a.addListener(o -> b.set(a.get()))):
- a is invalidated for the first time
- b is set to a.get() = x and b's invalidation listener is called for
the first time
- b is now valid
- a is invalidated for the second time
- b is set to a.get() = x and b's invalidation listener is *NOT*
called for the second time, because b's value did not change
In scenario A, b's invalidation listener is called twice, while in
scenario B just once. If "weird things" are happening in this
invalidation listener, this can result in different behavior.
Now, if b is ListCell.prefWidthProperty(), scenario A and scenario B
cause it to be invalidated different number of times, which, my
*speculation*, could yield different behavior.
This is how far I got.
Cheers,
Tomas
More information about the openjfx-dev
mailing list