[ListView] `b.bind(a)` not behaviorally equivalent to `a.addListener(o -> b.set(a.get()))`

Tomas Mikula tomas.mikula at gmail.com
Mon Dec 16 12:51:19 PST 2013


Never mind,

    b.bind(a);

should not be equivalent to

    a.addListener(o -> b.set(a.get()));

in the first place, but rather to

    a.addListener(o -> b.set(a.get()));
    b.set(a.get()); // don't forget this!

When I fixed my example, I'm not observing any difference in behavior.
I'm still not getting the behavior I want, but due to
https://javafx-jira.kenai.com/browse/RT-34897.

Thanks,
Tomas

On Mon, Dec 16, 2013 at 8:37 PM, Anthony Petrov
<anthony.petrov at oracle.com> wrote:
> Filing a bug never hurts. We'll either fix it, or close it and explain why
> it's not a bug then.
>
> --
> best regards,
> Anthony
>
>
> On 12/13/2013 11:17 PM, Tomas Mikula wrote:
>>
>> I guess my main question is: should I file a bug on ListView?
>>
>> Tomas
>>
>> On Fri, Dec 13, 2013 at 8:15 PM, Tomas Mikula <tomas.mikula at gmail.com>
>> wrote:
>>>
>>> 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