-
Bug
-
Resolution: Fixed
-
P4
-
8
-
Windows, using lombard build 92
Run the following code:
public class CellCreationBug extends Application {
// the number of tablecells that have been created
static int count = 0;
public static void main(String[] args) {
launch(args);
}
@Override public void start(Stage primaryStage) {
primaryStage.setTitle("Table Cell Creation Bug Demo");
StackPane root = new StackPane();
root.getChildren().add( buildTable() );
primaryStage.setScene(new Scene(root, 150, 250));
primaryStage.show();
}
private TableView buildTable() {
// create table with a bunch of Integer items
TableView<Integer> table = new TableView<>();
for ( int i = 1; i <= 500; i++ ) {
table.getItems().add(i);
}
// column to display those items
final TableColumn<Integer, Integer> column1 = new TableColumn<>("Column 1");
setupFactories( column1 );
column1.setPrefWidth( 100 );
table.getColumns().setAll( column1 );
return table;
}
// sets up a simple value factory for displaying integers in this table cell
private void setupFactories(TableColumn col) {
// simple value factory to display integer item in each table row
col.setCellValueFactory(
new Callback<CellDataFeatures<Integer, Integer>, ObservableIntegerValue>() {
public ObservableIntegerValue call(CellDataFeatures<Integer, Integer> p) {
return new ReadOnlyIntegerWrapper(p.getValue());
}
});
// simple cell factory that printlns a message each time it creates a tablecell
final Callback realFactory = col.getCellFactory();
col.setCellFactory(
new Callback<TableColumn<Integer,Integer>, TableCell<Integer,Integer>>() {
public TableCell<Integer, Integer> call(
TableColumn<Integer, Integer> c ) {
System.out.println("created cell #" + count++);
return (TableCell<Integer,Integer>)realFactory.call( c );
}
});
}
}
This creates a simple, 1-column table containing the integers from 1-500. This table prints a message to stdout every time it creates a new TableCell.
1) Notice that the Table has created 12 TableCells, which seems right, since that's how many cells I can see on the screen.
2) Grab the scrollbar button, and drag it up and down as much as you like. Notice that no new TableCells are created, which also seems right, given my understanding of how virtualized cells in the table work.
Now close and restart the program, and:
1) Move the mouse over the table, and use the mouse scrollwheel to scroll around in the table, instead of the scrollbar button.
2) Notice how a large number of additional TableCells are created. If I scroll quite quickly with the scrollwheel, I can get over 100 cells, even though only 12 are visible on the screen.
Given that there is no need to create new TableCells when the scrollbar scrolls, I'm pretty sure that new cells shouldn't be created for the scrollwheel either...? The "lost" cells also do not seem to be getting their 'item' property emptied out (set to null) when they are no longer in use by the table, so there is no easy way for them to attach and then detach listeners or eventhandlers to other nodes, etc, without causing a memory leak.
public class CellCreationBug extends Application {
// the number of tablecells that have been created
static int count = 0;
public static void main(String[] args) {
launch(args);
}
@Override public void start(Stage primaryStage) {
primaryStage.setTitle("Table Cell Creation Bug Demo");
StackPane root = new StackPane();
root.getChildren().add( buildTable() );
primaryStage.setScene(new Scene(root, 150, 250));
primaryStage.show();
}
private TableView buildTable() {
// create table with a bunch of Integer items
TableView<Integer> table = new TableView<>();
for ( int i = 1; i <= 500; i++ ) {
table.getItems().add(i);
}
// column to display those items
final TableColumn<Integer, Integer> column1 = new TableColumn<>("Column 1");
setupFactories( column1 );
column1.setPrefWidth( 100 );
table.getColumns().setAll( column1 );
return table;
}
// sets up a simple value factory for displaying integers in this table cell
private void setupFactories(TableColumn col) {
// simple value factory to display integer item in each table row
col.setCellValueFactory(
new Callback<CellDataFeatures<Integer, Integer>, ObservableIntegerValue>() {
public ObservableIntegerValue call(CellDataFeatures<Integer, Integer> p) {
return new ReadOnlyIntegerWrapper(p.getValue());
}
});
// simple cell factory that printlns a message each time it creates a tablecell
final Callback realFactory = col.getCellFactory();
col.setCellFactory(
new Callback<TableColumn<Integer,Integer>, TableCell<Integer,Integer>>() {
public TableCell<Integer, Integer> call(
TableColumn<Integer, Integer> c ) {
System.out.println("created cell #" + count++);
return (TableCell<Integer,Integer>)realFactory.call( c );
}
});
}
}
This creates a simple, 1-column table containing the integers from 1-500. This table prints a message to stdout every time it creates a new TableCell.
1) Notice that the Table has created 12 TableCells, which seems right, since that's how many cells I can see on the screen.
2) Grab the scrollbar button, and drag it up and down as much as you like. Notice that no new TableCells are created, which also seems right, given my understanding of how virtualized cells in the table work.
Now close and restart the program, and:
1) Move the mouse over the table, and use the mouse scrollwheel to scroll around in the table, instead of the scrollbar button.
2) Notice how a large number of additional TableCells are created. If I scroll quite quickly with the scrollwheel, I can get over 100 cells, even though only 12 are visible on the screen.
Given that there is no need to create new TableCells when the scrollbar scrolls, I'm pretty sure that new cells shouldn't be created for the scrollwheel either...? The "lost" cells also do not seem to be getting their 'item' property emptied out (set to null) when they are no longer in use by the table, so there is no easy way for them to attach and then detach listeners or eventhandlers to other nodes, etc, without causing a memory leak.