Search code examples
javatableviewchangelistener

Understanding adding a ChangeListener in TableView


I am trying to understand some code from the following source:

http://code.makery.ch/library/javafx-8-tutorial/part3/

The specific line i am still curious about is the following one:

personTable.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> showPersonDetails(newValue));

I believe that my first understanding of the javadoc was not correct; especially on this part ([...].selectedItemProperty().[...]):

https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/SelectionModel.html#selectedItemProperty--

I was asking myself why we are only adding one listener while we may have several dataobjects but now my understanding is the following and it would be nice to know if it is correct:

What the doc means is that "selectedItem" itself is the property (not the underlying data!)which represents a row that is selected/selection-changed at runtime, so the change method of our Listener is then evoked when the user changes the selected row. Then the synchronization with the underlying datamodel takes place via the change(...) method of the ChangeListener Interface, which gets the corresponding dataobjects from the udnerlying datamodel to work on. So from my understanding i would probably raise an exception if i didn't do a correct setItems(...) on my TableView.

Is this correct so far?

If yes, i have got a follow up question: ReadOnlyObjectProperty implements both Observable and ObservableValue which both have the method addListener. Is it correct that the Lambda-Expression is resolved correctly by checking the list of parameters of the two methods of the two possible functional interfaces which could be argument to either one of the addListener(...) methods? That point seemd rather complex to me.


Solution

  • The selectionModel.selectedItemProperty is a ReadOnlyObjectProperty<T> where T is the type of elements in the item list backing your TableView<T>. Whenever a new item is selected in the TableView, the value of the selectedItemProperty changes to refer to the new item. The selected item itself is not a TableRow<T>, it just a reference to the underlying data item which is used render the row. In general, you, as an application programmer, usually don't care about the TableRow which is a visual construct, but only the underlying data. When the user clicks on a row in the table, the TableView implementation sets the selectedItemProperty to the selected data item, that in turn fires any change listeners which have been set on the property.

    In the case of the makery example, the T type is Person. So the consequence of this line:

    personTable.getSelectionModel().selectedItemProperty().addListener(
        (observable, oldValue, newValue) -> showPersonDetails(newValue)
    );
    

    is to invoke the showPersonDetails(Person person) function, passing in the selected person whenever that changes.

    So from my understanding i would probably raise an exception if i didn't do a correct setItems(...) on my TableView.

    No. If you didn't set any items, the user could never select an item on the table, so you wouldn't ever get an exception from the selected item change listener as the selected item would never change from null and the change listener would never fire.

    i have got a follow up question: ReadOnlyObjectProperty implements both Observable and ObservableValue which both have the method addListener. Is it correct that the Lambda-Expression is resolved correctly by checking the list of parameters of the two methods of the two possible functional interfaces which could be argument to either one of the addListener(...) methods?

    Yes. How the lambda resolves which method to use which is complicated in compiler implementation, but from an application programmer usability point of view, you can just count the parameters: If it is one parameter, then an InvalidationListener is being defined, if it is 3 parameters, then a ChangeListener is being defined.

    There are two addListener(...) methods as it is an overloaded method, one for a ChangeListener and another for an InvalidationListener. The difference between the two is subtle and is explained by the developer of the interfaces with the recommendation: "use a ChangeListener if you need to know the new value in the listener, otherwise use an InvalidationListener".