ListViewのセルをカスタマイズする方法について書くよ。

1.ListViewのセルに埋め込むFXMLを作成する

例としてセルにテキストとボタンを入れてみる。
まずはセルに埋め込むFXMLをちゃっちゃと書いちゃいます。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
      xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="CellController">
    <children>
        <Label text="Label" fx:id="label"/>
        <Pane HBox.hgrow="ALWAYS"/>
        <Button mnemonicParsing="false" text="Button" fx:id="button"/>
    </children>
</HBox>

2.カスタムListCellを作る

ListViewに表示されるセルはListCellクラスでできています。
カスタマイズしたListCellを作りたい場合はこれを継承して新しいセルを作ります。
ここではCustomListCellという名前で作成します。

public class CustomListCell extends ListCell<Data>{
}

一緒にCellに表示するための情報を纏めたDataクラスを作成します。

public class Data {
    private final String text;

    public Data(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

3.FXMLを埋め込む

ListCellはListViewで表示されている範囲だけ作成されます。
ListViewのItemが100個あったとしても、実際に見えている部分が10個であればListCellは10個くらいしか存在しません。
スクロールやItemが更新された時にListCellのupdateitemが呼び出され、ListCellが表示内容を変更します。

public class CustomListCell extends ListCell<Data> {

    private CellController controller;

    @Override
    protected void updateItem(Data item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            //空になったので表示する内容はなし
            setGraphic(null);
            setText(null);
            controller = null;
            return;
        }

        if (controller == null || getGraphic() == null) {
            //コントローラかGraphicがなければFXMLからNodeと一緒に作成
            try {
                FXMLLoader fxmlLoader = new FXMLLoader();
                Node node = fxmlLoader.load(getClass().getResourceAsStream("cell.fxml"));
                controller = fxmlLoader.getController();
                setGraphic(node);
                controller.update(item);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        } else {
            //すでにある場合は新しく作らずに使い回し
            controller.update(item);
        }
    }
}

次にセルのコントローラを作成

public class CellController {
    @FXML
    private Label label;
    @FXML
    private Button button;

    public void update(Data item) {
        label.setText(item.getText());
        button.setOnAction(e -> System.out.println(item.getText()));
    }
}

4.CellFactoryを設定

ListView<Data> listView = new ListView<>();
listView.setCellFactory(param -> new CustomListCell());

おしまい。