{"id":1650,"date":"2014-11-04T17:10:07","date_gmt":"2014-11-04T16:10:07","guid":{"rendered":"http:\/\/www.jensd.de\/wordpress\/?p=1650"},"modified":"2015-11-22T20:54:18","modified_gmt":"2015-11-22T19:54:18","slug":"properties-extractor-best-way-to-make-the-listview-instantly-updating-its-elements","status":"publish","type":"post","link":"https:\/\/www.jensd.de\/wordpress\/?p=1650","title":{"rendered":"Properties Extractor: Best way to get the ListView instantly updating its elements"},"content":{"rendered":"<p>This post is about how to deal with JavaFX ListViews and TableViews and how these controls are getting informed about changed content of the contained elements.<br \/>\nI wonder why I didn&#8217;t find anything about the following pattern in the relevant books as this is a really crucial mechanism.<br \/>\nMany posts out there suggest to force triggering a ChangeEvent getting a ListView to refresh by calling<br \/>\n<code><br \/>\nlist.remove(POJO);<br \/>\nlist.add(index,POJO);<br \/>\n<\/code><br \/>\nafter each commited change! Brrr ;-)!<\/p>\n<p><strong>But there is a much better way:<\/strong><br \/>\nEnable your list to report changes on the element by providing an properties extractor.<\/p>\n<p><strong>The Demo App<\/strong><br \/>\nI have created a small demo app to play with for giving it a try.<br \/>\nBasically two TableViews and one ListView all sharing the same data.<br \/>\nTo change properties of the elements one TableView is editable:<br \/>\n<a href=\"http:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo1.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1657\" data-permalink=\"https:\/\/www.jensd.de\/wordpress\/?attachment_id=1657\" data-orig-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo1.png\" data-orig-size=\"784,507\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"PropertiesExtractor Demo1\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo1-300x194.png\" data-large-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo1.png\" tabindex=\"0\" role=\"button\" src=\"http:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo1.png\" alt=\"PropertiesExtractor Demo1\" width=\"784\" height=\"507\" class=\"aligncenter size-full wp-image-1657\" srcset=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo1.png 784w, https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo1-300x194.png 300w\" sizes=\"(max-width: 784px) 100vw, 784px\" \/><\/a><\/p>\n<p><strong>The DataModel<\/strong><\/p>\n<p><strong>The compulsive PersonBean folling the <a href=\"http:\/\/docs.oracle.com\/javafx\/2\/binding\/jfxpub-binding.htm\" title=\"http:\/\/docs.oracle.com\/javafx\/2\/binding\/jfxpub-binding.htm\" target=\"_blank\">JavaFX Bean Pattern\/Convention<\/a><\/strong><br \/>\n[java]<br \/>\npublic class PersonBean {<\/p>\n<p>    private StringProperty firstName;<br \/>\n    private StringProperty lastName;<br \/>\n    private ObjectProperty<LocalDate> birthday;<br \/>\n    private ObjectBinding<Long> age;<\/p>\n<p>    public PersonBean() {<br \/>\n    }<\/p>\n<p>    public PersonBean(String firstName, String lastName, LocalDate birthday) {<br \/>\n        setFirstName(firstName);<br \/>\n        setLastName(lastName);<br \/>\n        setBirthday(birthday);<br \/>\n    }<\/p>\n<p>    public final StringProperty firstNameProperty() {<br \/>\n        if (firstName == null) {<br \/>\n            firstName = new SimpleStringProperty();<br \/>\n        }<br \/>\n        return firstName;<br \/>\n    }<\/p>\n<p>    public final String getFirstName() {<br \/>\n        return firstNameProperty().get();<br \/>\n    }<\/p>\n<p>    public final void setFirstName(final java.lang.String firstName) {<br \/>\n        firstNameProperty().set(firstName);<br \/>\n    }<\/p>\n<p>    public final StringProperty lastNameProperty() {<br \/>\n        if (lastName == null) {<br \/>\n            lastName = new SimpleStringProperty();<br \/>\n        }<br \/>\n        return lastName;<br \/>\n    }<\/p>\n<p>    public final java.lang.String getLastName() {<br \/>\n        return lastNameProperty().get();<br \/>\n    }<\/p>\n<p>    public final void setLastName(final java.lang.String lastName) {<br \/>\n        lastNameProperty().set(lastName);<br \/>\n    }<\/p>\n<p>    public final ObjectProperty<LocalDate> birthdayProperty() {<br \/>\n        if (birthday == null) {<br \/>\n            birthday = new SimpleObjectProperty<>();<br \/>\n        }<br \/>\n        return birthday;<br \/>\n    }<\/p>\n<p>    public final LocalDate getBirthday() {<br \/>\n        return birthdayProperty().get();<br \/>\n    }<\/p>\n<p>    public final void setBirthday(final java.time.LocalDate birthday) {<br \/>\n        birthdayProperty().set(birthday);<\/p>\n<p>    }<\/p>\n<p>    public String stringValue() {<br \/>\n        return String.format(&#8220;%s %s %s&#8221;, getFirstName(), getLastName(), getBirthday().format(DateTimeFormatter.ISO_LOCAL_DATE));<br \/>\n    }<\/p>\n<p>    public final ObjectBinding<Long> ageBinding() {<br \/>\n        if (age == null) {<br \/>\n            age = new ObjectBinding<Long>() {<br \/>\n                {<br \/>\n                    bind(birthdayProperty());<br \/>\n                }<\/p>\n<p>                @Override<br \/>\n                protected Long computeValue() {<br \/>\n                    if (getBirthday() == null) {<br \/>\n                        return null;<br \/>\n                    }<br \/>\n                    return getBirthday().until(LocalDate.now(), ChronoUnit.YEARS);<br \/>\n                }<br \/>\n            };<br \/>\n        }<br \/>\n        return age;<br \/>\n    }<\/p>\n<p>    public static Callback<PersonBean, Observable[]> extractor() {<br \/>\n        return (PersonBean p) -> new Observable[]{p.lastNameProperty(), p.firstNameProperty(), p.birthdayProperty(), p.ageBinding()};<br \/>\n    }<br \/>\n}<br \/>\n[\/java]<\/p>\n<p>DataModel containing a List of randomly created PersonBeans:<br \/>\n[java]<br \/>\npublic class DataModel {<\/p>\n<p>    private ObservableList<PersonBean> personFXBeans;<\/p>\n<p>    public DataModel() {<br \/>\n        init();<br \/>\n    }<\/p>\n<p>    private void init() {<br \/>\n        personFXBeans = DataSource.getRandomPersonBeansList(100);<br \/>\n    }<\/p>\n<p>    public ObservableList<PersonBean> getPersonFXBeans() {<br \/>\n        return personFXBeans;<br \/>\n    }<br \/>\n}<br \/>\n[\/java]<\/p>\n<p>As you may know to assign a DataModel e.g. to a TableView or a ListView in JavaFX you just have to use the setItems(ObvervableList) method.<\/p>\n<p>[java]<br \/>\n@FXML<br \/>\npublic void onFillWithDemoDataFXBeans() {<br \/>\n  readOnlyListView.setItems(model.getPersonFXBeans());<br \/>\n  readOnlyTableView.setItems(model.getPersonFXBeans());<br \/>\n  editableTableView.setItems(model.getPersonFXBeans());<br \/>\n}<br \/>\n[\/java]<\/p>\n<p>Now getting a TableView informed about property changes of contained elements is already done you for by the binding either in two ways:<br \/>\nvia a <code><strong>PropertyValueFactory<\/strong><\/code> and by more or less direct property binding:<\/p>\n<p>[java]<br \/>\nreadOnlyFirstNameColumn.setCellValueFactory(new PropertyValueFactory<>(&#8220;firstName&#8221;));<br \/>\nreadOnlyLastNameColumn.setCellValueFactory(new PropertyValueFactory<>(&#8220;lastName&#8221;));<br \/>\nreadOnlyBirthdayColumn.setCellValueFactory(new PropertyValueFactory<>(&#8220;birthday&#8221;));<br \/>\nreadOnlyAgeColumn.setCellValueFactory(i -> i.getValue().ageBinding());<\/p>\n<p>editableFirstNameColumn.setCellValueFactory(i -> i.getValue().firstNameProperty());<br \/>\neditableLastNameColumn.setCellValueFactory(i -> i.getValue().lastNameProperty());<br \/>\neditableBirthdayColumn.setCellValueFactory(i -> i.getValue().birthdayProperty());<br \/>\nageColumn.setCellValueFactory(i -> i.getValue().ageBinding());<br \/>\n[\/java]<\/p>\n<p>But the ListView basically only observes the list and not the properties of each element of that list.<\/p>\n<p>When using a ObservableList created by FXCollections.observableArrayList() the ListView will only refresh on ListChange Events like remove() an add() of elements. Therefore<br \/>\n<code><br \/>\nlist.remove(POJO);<br \/>\nlist.add(index,POJO);<br \/>\n<\/code><br \/>\nafter each commited change.<\/p>\n<p><strong>But there is a much better way:<\/strong><br \/>\nEnable your list to report changes on the element by providing an properties extractor.<br \/>\nYou don&#8217;t have to care about refreshing then!<\/p>\n<p><strong>ObservableList<PersonBean> persons = FXCollections.observableArrayList(<em>PersonBean.extractor()<\/em>);<br \/>\n<\/strong><\/code><\/p>\n<p>See <code><strong>DataSource.getRandomPersonBeansList(int length)<\/strong><\/code>:<\/p>\n<p>[java]<br \/>\npublic static ObservableList&lt;PersonBean&gt; getRandomPersonBeansList(int length) {<br \/>\n        ObservableList&lt;PersonBean&gt;  persons = FXCollections.observableArrayList(PersonBean.extractor());<br \/>\n        for (int i = 0; i &lt; length; i++) {<br \/>\n            persons.add(new PersonBean(getRandomName(), getRandomLastname(), getRandomLocalDate()));<br \/>\n        }<br \/>\nreturn persons;<br \/>\n}<br \/>\n[\/java]<\/p>\n<p>This Extrator is basically a Callback containing an array of Obvervables which are then observed by the Obervablelist (more precicely: ObservableListWrapper):<\/p>\n<p>My <code><strong>PersonBean<\/strong><\/code> already provides it&#8217;s extrator callback:<\/p>\n<p>[java]<br \/>\npublic static Callback&lt;PersonBean, Observable[]&gt; extractor() {<br \/>\n   return (PersonBean p) -> new Observable[]{p.lastNameProperty(), p.firstNameProperty(), p.birthdayProperty(), p.ageBinding()};<br \/>\n}<br \/>\n[\/java]<\/p>\n<p>Following this pattern all controls are updated instantly after applying the change.<\/p>\n<p><strong>Edit data&#8230;<\/strong><br \/>\n<a href=\"http:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo3.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1665\" data-permalink=\"https:\/\/www.jensd.de\/wordpress\/?attachment_id=1665\" data-orig-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo3.png\" data-orig-size=\"782,399\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"PropertiesExtractor Demo3\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo3-300x153.png\" data-large-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo3.png\" tabindex=\"0\" role=\"button\" src=\"http:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo3.png\" alt=\"PropertiesExtractor Demo3\" width=\"782\" height=\"399\" class=\"aligncenter size-full wp-image-1665\" srcset=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo3.png 782w, https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo3-300x153.png 300w\" sizes=\"(max-width: 782px) 100vw, 782px\" \/><\/a><\/p>\n<p><strong>and commit:<\/strong><br \/>\n<a href=\"http:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo4.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1666\" data-permalink=\"https:\/\/www.jensd.de\/wordpress\/?attachment_id=1666\" data-orig-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo4.png\" data-orig-size=\"780,402\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"PropertiesExtractor Demo4\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo4-300x154.png\" data-large-file=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo4.png\" tabindex=\"0\" role=\"button\" src=\"http:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo4.png\" alt=\"PropertiesExtractor Demo4\" width=\"780\" height=\"402\" class=\"aligncenter size-full wp-image-1666\" srcset=\"https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo4.png 780w, https:\/\/www.jensd.de\/wordpress\/wp-content\/uploads\/2014\/11\/PropertiesExtractor-Demo4-300x154.png 300w\" sizes=\"(max-width: 780px) 100vw, 780px\" \/><\/a><\/p>\n<p><strong>THE CODE PLEASE! <\/strong><br \/>\nYou can find the complete code at my <a href=\"https:\/\/bitbucket.org\/Jerady\/propertiesextractordemo\/src\/6eb17ffcc7ee49794526ca45cbfb71100aa183a3\/src\/de\/jensd\/extractordemo\/?at=master\" title=\"https:\/\/bitbucket.org\/Jerady\/propertiesextractordemo\/src\/6eb17ffcc7ee49794526ca45cbfb71100aa183a3\/src\/de\/jensd\/extractordemo\/?at=master\" target=\"_blank\">BitBucket Repo<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is about how to deal with JavaFX ListViews and TableViews and how these controls are getting informed about changed content of the contained elements. I wonder why I didn&#8217;t find anything about the following pattern in the relevant books as this is a really crucial mechanism. Many posts out there suggest to force&hellip; <span class=\"clear\"><\/span><a href=\"https:\/\/www.jensd.de\/wordpress\/?p=1650\" class=\"more-link read-more\" rel=\"bookmark\">Continue Reading <span class=\"screen-reader-text\">Properties Extractor: Best way to get the ListView instantly updating its elements<\/span><i class=\"fa fa-arrow-right\"><\/i><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"footnotes":"","_jetpack_memberships_contains_paid_content":false,"jetpack_publicize_message":"Properties Extractor - Best way to make the ListView instantly updating its elements http:\/\/wp.me\/p38FCL-qC","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[51,52,10,4,8,7,49],"tags":[107,108,85,76,39,62,105],"jetpack_publicize_connections":[],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p38FCL-qC","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/1650"}],"collection":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1650"}],"version-history":[{"count":34,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/1650\/revisions"}],"predecessor-version":[{"id":2214,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/1650\/revisions\/2214"}],"wp:attachment":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1650"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1650"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1650"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}