Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define which property ListView should use to render

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"));
like image 735
JJ Roman Avatar asked Jul 06 '14 16:07

JJ Roman


2 Answers

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());
                }
            }
        };
    }
});
like image 64
James_D Avatar answered Nov 01 '22 10:11

James_D


Binding String Property

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:

  • Display - The underlying object behind the ListView
  • titleProperty - The JavaFX property to be displayed
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);
            }
        };
    }
);

Explanation

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.


TL;DR :: Modified James_D's Solution

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.

like image 34
NonlinearFruit Avatar answered Nov 01 '22 09:11

NonlinearFruit