Customize ListView in JavaFX with FXML

I understand your question. There are mainly two ways to set items in a Listview:

1. Create the ObservableList and set the items of the ListView with the ObservableList (listView.setItems(observableList)).

2. Use the setCellFactory() method of the ListView class.

You would prefer to use the setCellFactory() method, because this approach simplies the process as well as it helps to separate out the business logic and the UI (FXML).


Here is a more detailed explaination:


1. Create a new FXML file with the name listview.fxml to contain the ListView, and set the ListViewController class as its controller:

File: listview.fxml:

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.ListView?>
<?import demo.ListViewController?>

<GridPane xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
     <ListView fx:id="listView"/>
</GridPane>

2. Create the controller and name it ListViewController.
The controller can load the listview.fxml file and access the listview.

File: ListViewController.java:

package demo;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.io.IOException;
import java.util.Set;

public class ListViewController
{
    @FXML
    private ListView listView;
    private Set<String> stringSet;
    ObservableList observableList = FXCollections.observableArrayList();

    public ListViewController()
    {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listview.fxml"));
        fxmlLoader.setController(this);
        try
        {
            Parent parent = (Parent)fxmlLoader.load();
            Scene scene = new Scene(parent, 400.0 ,500.0);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void setListView()
    {
        stringSet.add("String 1");
        stringSet.add("String 2");
        stringSet.add("String 3");
        stringSet.add("String 4");
        observableList.setAll(stringSet);
        listView.setItems(observableList);
        listView.setCellFactory(new Callback<ListView<String>, javafx.scene.control.ListCell<String>>()
        {
            @Override
            public ListCell<String> call(ListView<String> listView)
            {
                return new ListViewCell();
            }
        });
    }
}

3. First you need to set the value of the ObservableList. This is very important.
Then, set the items of list using the ObservableList and call the setCellFactory() method on the ListView. In the given example I just take the String values an add them to the String set (the Set<String> stringSet).


4. When the setCellFactory() method is called on the ListView, it will return the ListCell. So for sake of simplicity, I added a class which extends the ListCell, and the setGraphic() method is present for the ListCell() and will set the items of the ListCell.

File: ListViewCell.java:

package demo;

import javafx.scene.control.ListCell;

public class ListViewCell extends ListCell<String>
{
    @Override
    public void updateItem(String string, boolean empty)
    {
        super.updateItem(string,empty);
        if(string != null)
        {
            Data data = new Data();
            data.setInfo(string);
            setGraphic(data.getBox());
        }
    }
}

5. I just added a class which will load the listCellItem.fxml and return the HBox, which will contain the other components as children.
The HBox is then set to the ListCell.

File: listCellItem.fxml:

 <?import demo.Data?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.control.Label?>

<HBox xmlns:fx="http://javafx.com/fxml" fx:id="hBox">
<children>
    <Label  fx:id="label1"/>
    <Label  fx:id="label2"/>
</children>
</HBox>

File: Data.java:

package demo;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import java.io.IOException;

public class Data
{
    @FXML
    private HBox hBox;
    @FXML
    private Label label1;
    @FXML
    private Label label2;

    public Data()
    {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listCellItem.fxml"));
        fxmlLoader.setController(this);
        try
        {
            fxmlLoader.load();
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void setInfo(String string)
    {
        label1.setText(string);
        label2.setText(string);
    }

    public HBox getBox()
    {
        return hBox;
    }
}

Using this way, you can use the setCellFactory() method to separate the things that are business logic and FXML.

Hope this is helpful.

Leave a Comment