How can I Populate a ListView in JavaFX using Custom Objects?

Solution Approach

I advise using a cell factory to solve this problem.

listViewOfWords.setCellFactory(param -> new ListCell<Word>() {
    @Override
    protected void updateItem(Word item, boolean empty) {
        super.updateItem(item, empty);

        if (empty || item == null || item.getWord() == null) {
            setText(null);
        } else {
            setText(item.getWord());
        }
    }
});

Sample Application

add image

import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class CellFactories extends Application {    
    @Override
    public void start(Stage stage) {
        ObservableList<Word> wordsList = FXCollections.observableArrayList();
        wordsList.add(new Word("First Word", "Definition of First Word"));
        wordsList.add(new Word("Second Word", "Definition of Second Word"));
        wordsList.add(new Word("Third Word", "Definition of Third Word"));
        ListView<Word> listViewOfWords = new ListView<>(wordsList);
        listViewOfWords.setCellFactory(param -> new ListCell<Word>() {
            @Override
            protected void updateItem(Word item, boolean empty) {
                super.updateItem(item, empty);

                if (empty || item == null || item.getWord() == null) {
                    setText(null);
                } else {
                    setText(item.getWord());
                }
            }
        });
        stage.setScene(new Scene(listViewOfWords));
        stage.show();
    }

    public static class Word {
        private final String word;
        private final String definition;

        public Word(String word, String definition) {
            this.word = word;
            this.definition = definition;
        }

        public String getWord() {
            return word;
        }

        public String getDefinition() {
            return definition;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Implementation Notes

Although you could override toString in your Word class to provide a string representation of the word aimed at representation in your ListView, I would recommend providing a cell factory in the ListView for extraction of the view data from the word object and representation of it in your ListView. Using this approach you get separation of concerns as you don’t tie a the graphical view of your Word object with it’s textual toString method; so toString could continue to have different output (for example full information on Word fields with a word name and a description for debugging purposes). Also, a cell factory is more flexible as you can apply various graphical nodes to create a visual representation of your data beyond just a straight text string (if you wish to do that).

Also, as an aside, I recommend making your Word objects immutable objects, by removing their setters. If you really need to modify the word objects themselves, then the best way to handle that is to have exposed observable properties for the object fields. If you also want your UI to update as the observable properties of your objects change, then you need to make your list cells aware of the changes to the associated items, by listening for changes to them (which is quite a bit more complex in this case). Note that, the list containing the words is already observable and ListView will take care of handling changes to that list, but if you modified the word definition for instance within a displayed word object, then your list view wouldn’t pick up changes to the definition without appropriate listener logic in the ListView cell factory.

Leave a Comment