javajavafxjavafx-9

JavaFX JDK 9.0.4 ListView celFactory adds null cells


Why does the CellFactory add so many null elements in this list? I explicitly set an observable array with just "a" and "b"
I don't think it's a problem with the bindings ... Any suggestions?

package at.kingcastle.misc;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class MainSpielwiese  extends Application {
    @Override
    public void start(Stage primaryStage) {
        ListView<String> lv = new ListView<>();
        lv.setItems(FXCollections.observableArrayList(new String[] {"a", "b"}));

        StackPane root = new StackPane();
        root.getChildren().add(lv);

        Scene scene = new Scene(root, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();

        lv.setCellFactory(list -> {
            ListCell<String> cell = new ListCell<>();
            ContextMenu contextMenu = new ContextMenu();
            cell.textProperty().bind(Bindings.format("%s", cell.itemProperty()));
            return cell;
        });
    }

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

enter image description here


Solution

  • Empty cells always have null as their item.

    A string format will format null as the literal string "null" (the string containing the four characters n, u, l, and l). Consequently, your binding will display the text "null" in all the empty cells.

    Since you have string data in this column, you can just do

    cell.textProperty().bind(cell.itemProperty());
    

    which will set the text to the value null instead of the literal string "null" when the cell is empty.

    More generally (i.e. for data types that are not String, so you can't use the binding above), you can do something like

    cell.textProperty().bind(Bindings.
        when(cell.emptyProperty()).
        then("").
        otherwise(Bindings.format("%s", cell.itemProperty())));
    

    or

    cell.textProperty().bind(Bindings.createStringBinding(() -> {
        if (cell.isEmpty()) {
            return "" ;
        } else {
            return String.format("%s", cell.getItem());
        }
    }, cell.itemProperty(), cell.emptyProperty());
    

    or

    cell.textProperty().bind(new StringBinding() {
        {
            bind(cell.textProperty(), cell.emptyProperty());
        }
        @Override
        public String computeValue() {
            return cell.isEmpty() ? "" : String.format("%s", cell.getItem()) ;
        }
    });