Adding and modifying a List of Strings in a JavaFx ListView is a relatively easy task, although may not be completely intuitive at first glance. In order to have changes to a List automatically reflected in a ListView you will need to make use of the ListProperty class, however setting items in the property is not as easy as calling the add() or addAll() methods. This is because the underlying ObservableList class that the Property is using does not support the add() or addAll() methods. Instead, you will need to first wrap your List in an ObservableList, and then pass that ObservableList to the ListProperty instance.
Below is an small sample application which illustrates this point. There are two ArrayLists which contain Strings of data. One contains the currency codes of some Asian currencies, and the other contains the currency codes of European currencies. The list should initially be loaded with the Asian currencies, and when the user clicks on the Button, the handleButtonAction() method is invoked, which will cause the European currency list to be rendered in the ListView.
First, the FXML layout for the UI.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?import java.lang.*?> | |
<?import java.util.*?> | |
<?import javafx.scene.*?> | |
<?import javafx.scene.control.*?> | |
<?import javafx.scene.layout.*?> | |
<AnchorPane id="AnchorPane" prefHeight="201.0" prefWidth="131.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="com.lynden.testjavafxlistview.AppController"> | |
<children> | |
<Button fx:id="button" layoutX="14.0" layoutY="160.0" onAction="#handleButtonAction" prefHeight="26.0" prefWidth="100.0" text="Click Me!" /> | |
<ListView fx:id="myListView" layoutX="14.0" layoutY="21.0" prefHeight="109.0" prefWidth="100.0" /> | |
</children> | |
</AnchorPane> |
Next, the controller class for the application
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.lynden.testjavafxlistview; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.ResourceBundle; | |
import javafx.beans.property.ListProperty; | |
import javafx.beans.property.SimpleListProperty; | |
import javafx.collections.FXCollections; | |
import javafx.event.ActionEvent; | |
import javafx.fxml.FXML; | |
import javafx.fxml.Initializable; | |
import javafx.scene.control.Button; | |
import javafx.scene.control.Label; | |
import javafx.scene.control.ListView; | |
public class AppController implements Initializable { | |
@FXML | |
private Button button; | |
@FXML | |
private Label label; | |
@FXML | |
private ListView myListView; | |
protected List<String> asianCurrencyList = new ArrayList<>(); | |
protected List<String> europeanCurrencyList = new ArrayList<>(); | |
protected ListProperty<String> listProperty = new SimpleListProperty<>(); | |
@FXML | |
private void handleButtonAction(ActionEvent event) { | |
listProperty.set(FXCollections.observableArrayList(europeanCurrencyList)); | |
} | |
@Override | |
public void initialize(URL url, ResourceBundle rb) { | |
asianCurrencyList.add("CNH"); | |
asianCurrencyList.add("JPY"); | |
asianCurrencyList.add("HKD"); | |
asianCurrencyList.add("KRW"); | |
asianCurrencyList.add("SGD"); | |
europeanCurrencyList.add("EUR"); | |
europeanCurrencyList.add("GBP"); | |
europeanCurrencyList.add("NOK"); | |
europeanCurrencyList.add("SEK"); | |
europeanCurrencyList.add("CHF"); | |
europeanCurrencyList.add("HUF"); | |
myListView.itemsProperty().bind(listProperty); | |
//This does not work, you can not directly add to a ListProperty | |
//listProperty.addAll( asianCurrencyList ); | |
listProperty.set(FXCollections.observableArrayList(asianCurrencyList)); | |
} | |
} |
The two lists are populated within the initialize() method of the controller. As you can see it is not possible simply to call the addAll() method on the ListProperty, as it will result in an “OperationNotSupportedException”. Instead, at line 57, the FXCollections class is used to wrap the ArrayList in an ObservableArrayList instance, which is then passed into the ListProperty.
Finally, in the handleButtonAction() method, the europeanCurrencyList is wrapped in ObservableList, and passed to the ListProperty. Since the ListProperty and the ListView are bound together (at line 53), the ListView is automatically updated with the European currency values.
Below are screenshots of the sample app both before and after the button has been clicked.
twitter: @RobTerpilowski
LinkedIn Profile: Rob Terpilowski
Pingback: Java desktop links of the week, September 28 « Jonathan Giles
Simple and clear. Thanks.
Most comprehensive explanation I found so far. Excellent article
Thanks!
What you’re doing is creating a new ObervableList every time the action is performed.
What you need to do instead is instantiate SimpleListProperty with a list implementation, this way support is provided by the backing list:
protected SimpleListProperty listProperty
= new SimpleListProperty(FXCollections.observableArrayList());
Now in your handleButtonAction you can simply addAll
listProperty.addAll(europeanCurrencyList);
Just a follow up the pattern should be to expose the API methods of the backing observable collection to your controller methods instead of calling on SimpleListProperty:
private final ObservableList observableList
= FXCollections.observableArrayList();
private final SimpleListProperty listProperty
= new SimpleListProperty(observableList);
now you can addAll on observableList.
Thanks for the tip Brett!