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:
How ever if delete a row ex: the first row (123-abc), the ListView
will look like this:
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.
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();
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With