Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListView using custom cell factory doesn't update after items deleted

I'm learning JavaFX and i wanted to create a cell factory which is working properly until i want to delete a row from my ListView:

plateList.setCellFactory(new Callback<ListView<Car>, ListCell<Car>>() {

    @Override
    public ListCell<Car> call(ListView<Car> param) {
        ListCell<Car> cell = new ListCell<Car>() {

            @Override
            protected void updateItem(Car item, boolean empty) {
                super.updateItem(item, empty);
                if (item != null) {
                    setText(item.getPlate());
                }
            }
        };
        return cell;
    }
});

I'm populating the ListView with some sample data:

ObservableList<Car> sample = FXCollections.observableArrayList();
sample.add(new Car("123-abc", "opel", "corsa", 5.5));
sample.add(new Car("123-cba", "vw", "passat", 7.5));
plateList.setItems(sample);

Now i will see what i expect the ListView will be the following:

  • 123-abc
  • 123-cba

How ever if delete a row ex: the first row (123-abc), the ListView will look like this:

  • 123-cba
  • 123-cba

This is the delete part:

@FXML
private void deleteBtnAction() {
    plateList.getItems().remove(plateList.getSelectionModel().getSelectedItem());
    ObservableList<Car> t = plateList.getItems();
    plateList.setItems(t);
}

If i remove the cell factory the program works as intended. Any help is greatly appreciated.

like image 440
Wermerb Avatar asked Feb 07 '15 16:02

Wermerb


2 Answers

Try changing to the following, This is required as JavaFX reuses the list cells, so the updateItem() needs to blank unused ones too when passed null

super.updateItem(item, empty);
if (item != null) {
   setText(item.getPlate());
} else {
   setText("");   // <== clear the now empty cell.
}

Full SSCCE

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class JavaFxListView extends Application {

    private static class Car {
        private String plate;

        public Car(String plate, String string2, String string3, double d) {
            this.plate = plate;
        }

        public String getPlate() {
            return plate;
        }

    }

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

    @Override
    public void start(Stage arg0) throws Exception {
        ListView<Car> plateList = new ListView<Car>();
        plateList.setCellFactory(new Callback<ListView<Car>, ListCell<Car>>() {

            @Override
            public ListCell<Car> call(ListView<Car> param) {
                ListCell<Car> cell = new ListCell<Car>() {

                    @Override
                    protected void updateItem(Car item, boolean empty) {
                        super.updateItem(item, empty);
                        if (item != null) {
                            setText(item.getPlate());
                        } else {
                            setText("");
                        }
                    }
                };
                return cell;
            }
        });
        Button delete = new Button("Delete");
        ObservableList<Car> sample = FXCollections.observableArrayList();
        sample.add(new Car("123-abc", "opel", "corsa", 5.5));
        sample.add(new Car("123-cba", "vw", "passat", 7.5));

        delete.setOnAction((e) -> {
            plateList.getItems().remove(plateList.getSelectionModel().getSelectedItem());
            ObservableList<Car> t = plateList.getItems();
            plateList.setItems(t);
        });

        plateList.setItems(sample);
        arg0.setScene(new Scene(new VBox(plateList, delete)));
        arg0.show();
    }
}
like image 77
Adam Avatar answered Nov 17 '22 08:11

Adam


According to the Java doc of Cell updateItem method, there is slightly different recomended usage than the accepted one:

protected void updateItem(T item, boolean empty) {
    super.updateItem(item, empty);

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

The difference is in the usage of parameter empty. But the sollution from @Adam should work correctly in major cases too.

like image 5
Zavael Avatar answered Nov 17 '22 08:11

Zavael