I've noticed that when I use a custom list cell factory, there's a point where if my ListCell(s) are instantiated too slowly it causes problems with the way ListView scrolls. It's most noticeable when using a scroll wheel and scrolling very rapidly. It's probably not possible to scroll quickly enough with a ratcheted scroll wheel to observe the issue. A mouse with a friction-less scroll wheel or a track pad with circular scrolling support should make the issue easier to observe.
When running the below code, I've seen up to 800+ instances of CustomListCell created while scrolling. None of them seem to be eligible for garbage collection either. If I spin my scroll wheel very quickly, the UI appears to freeze while the (hundreds of) instances of CustomListCell are created. The number of instances created depends on how long I continue to spin the mouse wheel.
The application starts with 18 instances of CustomListCell and my initial expectation was that 18 instances of CustomListCell is all that would ever be needed unless the UI was re-sized to show more rows.
I think this is a bug because after the initial 'pile up' of CustomListCell creation, I'm able to scroll the list in every manner I can think of and it's very fluid, even in cases where less than 100 instances of CustomListCell were available.
When I scroll using the keyboard, the UI stutters until there are 20 instances of CustomListCell and then it's very fluid when continuing to scroll with the keyboard. Scrolling via the arrow keys on the scroll bar is very similar.
Scrolling by grabbing the scroll bar and dragging it around is also similar to using the keyboard. When the list is initially shown, there are 18 instances of CustomListCell. When I grab the scroll bar and drag it downward, the UI freezes while a 19th CustomListCell is created. After that, scrolling via dragging the scroll bar is fluid.
To me, that indicates it should be possible to scroll the ListView using any method with about ~20 instances of CustomListCell. However, even when I remove the artificial delay in the below example, I'm still able to get the instance count of CustomListCell to grow slightly by flicking my mouse wheel very quickly. It's much less obvious though; the largest instance count I was able to get without an artificial delay was 42.
http://pastebin.com/80NWUdVf
{code:java}
import com.sun.javafx.runtime.VersionInfo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SlowInitCell extends Application {
@Override
public void start(Stage stage) throws Exception {
ListView<String> listView = new ListView<>();
listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> stringListView) {
return new CustomListCell();
}
});
for(int i=0;i<10000;i++) {
listView.getItems().add("List item " + i);
}
stage.setTitle("List View Scrolling");
stage.setScene(new Scene(listView));
stage.show();
}
public static void main(String[] args) {
System.out.println(VersionInfo.getRuntimeVersion());
launch(args);
}
private class CustomListCell extends ListCell<String> {
private final Label label = new Label();
private CustomListCell() {
try {
Thread.sleep(50);
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void updateItem(String s, boolean empty) {
super.updateItem(s, empty);
if(!empty) {
label.setText(s);
setGraphic(label);
}
}
}
}
{code}
When running the below code, I've seen up to 800+ instances of CustomListCell created while scrolling. None of them seem to be eligible for garbage collection either. If I spin my scroll wheel very quickly, the UI appears to freeze while the (hundreds of) instances of CustomListCell are created. The number of instances created depends on how long I continue to spin the mouse wheel.
The application starts with 18 instances of CustomListCell and my initial expectation was that 18 instances of CustomListCell is all that would ever be needed unless the UI was re-sized to show more rows.
I think this is a bug because after the initial 'pile up' of CustomListCell creation, I'm able to scroll the list in every manner I can think of and it's very fluid, even in cases where less than 100 instances of CustomListCell were available.
When I scroll using the keyboard, the UI stutters until there are 20 instances of CustomListCell and then it's very fluid when continuing to scroll with the keyboard. Scrolling via the arrow keys on the scroll bar is very similar.
Scrolling by grabbing the scroll bar and dragging it around is also similar to using the keyboard. When the list is initially shown, there are 18 instances of CustomListCell. When I grab the scroll bar and drag it downward, the UI freezes while a 19th CustomListCell is created. After that, scrolling via dragging the scroll bar is fluid.
To me, that indicates it should be possible to scroll the ListView using any method with about ~20 instances of CustomListCell. However, even when I remove the artificial delay in the below example, I'm still able to get the instance count of CustomListCell to grow slightly by flicking my mouse wheel very quickly. It's much less obvious though; the largest instance count I was able to get without an artificial delay was 42.
http://pastebin.com/80NWUdVf
{code:java}
import com.sun.javafx.runtime.VersionInfo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SlowInitCell extends Application {
@Override
public void start(Stage stage) throws Exception {
ListView<String> listView = new ListView<>();
listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> stringListView) {
return new CustomListCell();
}
});
for(int i=0;i<10000;i++) {
listView.getItems().add("List item " + i);
}
stage.setTitle("List View Scrolling");
stage.setScene(new Scene(listView));
stage.show();
}
public static void main(String[] args) {
System.out.println(VersionInfo.getRuntimeVersion());
launch(args);
}
private class CustomListCell extends ListCell<String> {
private final Label label = new Label();
private CustomListCell() {
try {
Thread.sleep(50);
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void updateItem(String s, boolean empty) {
super.updateItem(s, empty);
if(!empty) {
label.setText(s);
setGraphic(label);
}
}
}
}
{code}