The observed problem:
I want to use a ListView to display complex objects. In the code below I used a simple StringWrapper Class to simulate the observed behaviour. When an item is deleted from the ListView, though internaly it is removed from the associated ObservableList, the user will still be shown the item in the list. However that item will not be selectable anymore. In fact, the list will always show the same amount of items (in the end very random items) until the last item has been deleted. With the deletion of the last item the list will be cleared.
The Problem only occurs when an own CellFactory is used. Furthermore, the same code runs just fine with JDK 7.
Here is the Code:
package listviewbug;
import java.util.ArrayList;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
*
* @author me
*/
public class ListViewBug extends Application {
@FXML
ListView<StringWrapper> listView;
@FXML
Button removeButton;
ObservableList<StringWrapper> stringList;
@Override
public void start(Stage primaryStage) {
//create a new ListView
listView = new ListView();
listView.autosize();
//intial set up for the example ListView
fillList();
//button to delete items from the list
Button bt_delete = new Button();
bt_delete.setText("delete selected");
bt_delete.setOnAction((ActionEvent event) -> {
if (listView.getSelectionModel().getSelectedIndex() == -1) {
System.out.println("NOTHING SELECTED!");
} else {
stringList.remove(listView.getSelectionModel().getSelectedItem());
printInfo();
}
});
//button to fill list up again, start test over.
Button bt_fillList = new Button();
bt_fillList.setText("fill list");
bt_fillList.setOnAction((ActionEvent event) -> fillList());
//add elements to stack pane
FlowPane root = new FlowPane();
VBox vbox = new VBox(bt_delete, bt_fillList);
root.getChildren().add(vbox);
root.getChildren().add(new Pane(listView));
Scene scene = new Scene(root, 400, 250);
primaryStage.setTitle("ListView Bug");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* Fills list with some example StringWrappers
*/
public void fillList() {
//Create some test StringWrapper
ArrayList<StringWrapper> StringWrapper = new ArrayList();
StringWrapper.add(new StringWrapper("a"));
StringWrapper.add(new StringWrapper("b"));
StringWrapper.add(new StringWrapper("c"));
StringWrapper.add(new StringWrapper("d"));
StringWrapper.add(new StringWrapper("e"));
//add StringWrapper to an ObservableList
stringList = FXCollections.observableArrayList(StringWrapper);
//set items of the ObservableList for the ListView
listView.setItems(stringList);
//set how the StringWrapper will be displayed
listView.setCellFactory((ListView<StringWrapper> p) -> new StringWrapperEventCell());
printInfo();
}
/**
* A ListCell displaying a StringWrapper. The List Wrapper is simply
* converted to a String and then put into a Text object.
*/
private static class StringWrapperEventCell extends ListCell<StringWrapper> {
@Override
protected void updateItem(StringWrapper se, boolean bln) {
super.updateItem(se, bln); //To change body of generated methods, choose Tools | Templates.
if (se != null) {
Text t = new Text(se.toString());
setGraphic(t);
}
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
/**
* StringWrapper Class to represent a complex Object.
*/
public class StringWrapper {
String string;
public StringWrapper(String string) {
this.string = string;
}
@Override
public String toString() {
return string;
}
}
public void printInfo() {
System.out.println("+++++++++++++++++++++++++++++++");
System.out.println("ListView Size: " + listView.getItems().size());
System.out.println("ObservableList Size: " + stringList.size());
System.out.println("+++++++++++++++++++++++++++++++");
}
}
</AnchorPane>
I want to use a ListView to display complex objects. In the code below I used a simple StringWrapper Class to simulate the observed behaviour. When an item is deleted from the ListView, though internaly it is removed from the associated ObservableList, the user will still be shown the item in the list. However that item will not be selectable anymore. In fact, the list will always show the same amount of items (in the end very random items) until the last item has been deleted. With the deletion of the last item the list will be cleared.
The Problem only occurs when an own CellFactory is used. Furthermore, the same code runs just fine with JDK 7.
Here is the Code:
package listviewbug;
import java.util.ArrayList;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
*
* @author me
*/
public class ListViewBug extends Application {
@FXML
ListView<StringWrapper> listView;
@FXML
Button removeButton;
ObservableList<StringWrapper> stringList;
@Override
public void start(Stage primaryStage) {
//create a new ListView
listView = new ListView();
listView.autosize();
//intial set up for the example ListView
fillList();
//button to delete items from the list
Button bt_delete = new Button();
bt_delete.setText("delete selected");
bt_delete.setOnAction((ActionEvent event) -> {
if (listView.getSelectionModel().getSelectedIndex() == -1) {
System.out.println("NOTHING SELECTED!");
} else {
stringList.remove(listView.getSelectionModel().getSelectedItem());
printInfo();
}
});
//button to fill list up again, start test over.
Button bt_fillList = new Button();
bt_fillList.setText("fill list");
bt_fillList.setOnAction((ActionEvent event) -> fillList());
//add elements to stack pane
FlowPane root = new FlowPane();
VBox vbox = new VBox(bt_delete, bt_fillList);
root.getChildren().add(vbox);
root.getChildren().add(new Pane(listView));
Scene scene = new Scene(root, 400, 250);
primaryStage.setTitle("ListView Bug");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* Fills list with some example StringWrappers
*/
public void fillList() {
//Create some test StringWrapper
ArrayList<StringWrapper> StringWrapper = new ArrayList();
StringWrapper.add(new StringWrapper("a"));
StringWrapper.add(new StringWrapper("b"));
StringWrapper.add(new StringWrapper("c"));
StringWrapper.add(new StringWrapper("d"));
StringWrapper.add(new StringWrapper("e"));
//add StringWrapper to an ObservableList
stringList = FXCollections.observableArrayList(StringWrapper);
//set items of the ObservableList for the ListView
listView.setItems(stringList);
//set how the StringWrapper will be displayed
listView.setCellFactory((ListView<StringWrapper> p) -> new StringWrapperEventCell());
printInfo();
}
/**
* A ListCell displaying a StringWrapper. The List Wrapper is simply
* converted to a String and then put into a Text object.
*/
private static class StringWrapperEventCell extends ListCell<StringWrapper> {
@Override
protected void updateItem(StringWrapper se, boolean bln) {
super.updateItem(se, bln); //To change body of generated methods, choose Tools | Templates.
if (se != null) {
Text t = new Text(se.toString());
setGraphic(t);
}
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
/**
* StringWrapper Class to represent a complex Object.
*/
public class StringWrapper {
String string;
public StringWrapper(String string) {
this.string = string;
}
@Override
public String toString() {
return string;
}
}
public void printInfo() {
System.out.println("+++++++++++++++++++++++++++++++");
System.out.println("ListView Size: " + listView.getItems().size());
System.out.println("ObservableList Size: " + stringList.size());
System.out.println("+++++++++++++++++++++++++++++++");
}
}
</AnchorPane>
- relates to
-
JDK-8278904 Cell: misleading doc of updateItem
- Open