I have a java bean object list which I would like to display in ListView control. By default ListView uses toString method.
How can I define which property to use for rendering in ListView?
I want to achieve same functionality as in TableView can be achieved by PropertyValueFactory in this code:
@FXML
private TableView<Person> mainTableView;
//...
TableColumn<Person,String> personColumn = new TableColumn<>("Name");List
personColumn.setCellValueFactory(new PropertyValueFactory("name"));
mainTableView.getColumns().add(personColumn);
Edit
It looks like there is no easy(out of the box) solution. Based on code from James_D I created generic class to deal with the problem. It wraps PropertyValueFactory - note that PropertyValueFactory firstly looks for method [NAME]Property() trying to get observable, only when it is not found it tries to access standard bean properties.
public class PropertyValueFactoryWrapperCellFactory<T> implements Callback<ListView<T>, ListCell<T>> {
private final PropertyValueFactory<T, String> pvf;
public PropertyValueFactoryWrapperCellFactory(String propertyName) {
super();
pvf = new PropertyValueFactory(propertyName);
}
@Override
public ListCell<T> call(ListView<T> param) {
return new ListCell<T>() {
@Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
textProperty().unbind();
if (item == null) {
return;
}
TableColumn.CellDataFeatures<T, String> cdf = new TableColumn.CellDataFeatures<>(null, null, item);
textProperty().bind(pvf.call(cdf));
}
};
}
}
Usage:
@FXML
private ListView<Person> mainListView;
//...
mainListView.setCellFactory(new PropertyValueFactoryWrapperCellFactory("name"));
Use a cell factory.
If the property is immutable, it's pretty straightforward:
ListView<MyDataType> listView = new ListView<>();
listView.setCellFactory(new Callback<ListView<MyDataType>, ListCell<MyDataType>>() {
@Override
public ListCell<MyDataType> call(ListView<MyDataType> lv) {
return new ListCell<MyDataType>() {
@Override
public void updateItem(MyDataType item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setText(null);
} else {
// assume MyDataType.getSomeProperty() returns a string
setText(item.getSomeProperty());
}
}
};
}
});
If the property can change its value and the list view needs to update dynamically in response to these changes, you need to bind the textProperty
of the list cell:
listView.setCellFactory(new Callback<ListView<MyDataType>, ListCell<MyDataType>>() {
@Override
public ListCell<MyDataType> call(ListView<MyDataType> lv) {
return new ListCell<MyDataType>() {
@Override
public void updateItem(MyDataType item, boolean empty) {
super.updateItem(item, empty);
textProperty().unbind();
if (item == null) {
setText(null);
} else {
// assumes MyDataType.someProperty() returns a StringProperty:
textProperty.bind(item.someProperty());
}
}
};
}
});
This code allows you to choose what JavaFX property is displayed in a ListView. I use an anonymous class inside a lambda to add some clarity to what is happening. In this example:
listView.setCellFactory(
listview -> {
return new ListCell<Display>() {
@Override
public void updateItem(Display item, boolean empty) {
super.updateItem(item, empty);
textProperty().unbind();
if(item != null)
textProperty().bind(item.titleProperty());
else
setText(null);
}
};
}
);
With this code, we are basically making a custom ListCell. When it is updated and the item it is display is null
, we clear the display text. Otherwise, we set the text to be whatever the title property of our item is.
I based this on James_D's second example. I wanted to bind a SimpleStringProperty
to be the displayed text in a ListView. James_D's solution worked great but didn't update when I deleted an object from the ObservableList
in the ListView, so I modified it. I also thought having a cleaner lambda example would be good.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With