Weird reworking of cells in ListView - JavaFX

What I understand from the JavaFX documentation is that when there are not enough cells to fill the provided ListView (or seemingly) new cells are generated. Otherwise, existing cells are reused by calling the updateItem method for each one with matching items.

I am using a chat app where the user writes something in the textarea, presses enter and the chat is added to the history above the textarea, very simple and classic. History is a ListView showing a list of chats sent / received so far. So when the user hits the enter button, I add a new item to the underlying observable list, so it shows up in the ListView.

The problem is when adding a new chat to the ListView there is some blinking in the list, it takes a little time to update the items in the list. However, the newly added chat is at the end of the list, and it is not visible to the user (now you need to scroll down). So you don't really need to update the already visible cells , but it does. I tried to simplify the content of the updateItem method, but nothing ... It always blinks.

Then I applied a simple class:

public class IdGenerator {
    private static IdGenerator instance = new IdGenerator();
    private int id = 0;

    public static IdGenerator getInstance() {
        return instance;
    }

    public synchronized int getId() {
        return ++id;
    }
}

      

and then assigned ids to the generated cells:

public class CommunicationEntityCell extends ListCell<CommunicationEntity> {

    ...
    private int id = IdGenerator.getInstance().getId();
    ....

    @Override
    protected synchronized void updateItem(CommunicationEntity entity, boolean empty) {
        super.updateItem(entity, empty);
        System.out.println(id);
        ....
    }
}

      

And I noticed that when a new item is added to the underlying list, the cells are played rather than recycled . (And this is probably the reason for this blinking, anyway)

Is it normal / logical / expected to reproduce cells? Is this a (known) bug? Have you ever seen this? Any workaround would be much appreciated.

+1


source to share


2 answers


I created a similar test:

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ListCellReuseTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        final BorderPane root = new BorderPane();
        final ListView<String> list = new ListView<>();
        final Button addButton = new Button("Add message");
        addButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                int count = list.getItems().size() + 1 ;
                System.out.println("Creating message "+count);
                list.getItems().add("Message "+count);
            }
        });
        list.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {

            @Override
            public ListCell<String> call(ListView<String> listView) {
                return new TestListCell();
            }

        });

        root.setCenter(list);
        root.setBottom(addButton);
        primaryStage.setScene(new Scene(root, 200, 400));
        primaryStage.show();
    }

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

    public static class TestListCell extends ListCell<String> {
        private static int nextId ;
        private final int id ;
        public TestListCell() {
            id = ++nextId ;
        }
        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            setText(item);
            System.out.println(id);
        }

    }

}

      

In JavaFX 2.2, this seems to create a huge number of ListCells (17 new cells every time I click the button, displays 16 cells) and calls updateItem (...) even more times. I guess a lot of these cells go out of scope pretty quickly and get garbage collected.



Running the same code in JavaFX 8 shows very different behavior; it creates 17 cells the first time the button is pressed and doesn't create new ones after that. There are far fewer calls to updateItem (...), and not when the new cell is not displayed. It seems like it should be more efficient and definitely more intuitive and closer to what I expect.

I don't see any of the "blinking" ones you report, although in JavaFX 2.2 or JavaFX 8; so it's not immediately clear that this is directly related to the behavior and reuse of cells. It might be that the rest of your updateItem (...) method might be more efficient (like complex cache graphics rather than recreating them every time), or that there is something else that is causing this.

Do you see the same thing in JavaFX 8?

+2


source


You don't seem to need an API ListView

for your application. ListView

is intended not for displaying widgets in a vertical area, but for binding a collection to a widget. In your case, I just used VBox

wrapped in ScrollPane

and will add new children as soon as they are confirmed by the chat system.

The comments clarify that you have chosen the API ListView

to improve memory usage. Well, keeping a fixed number of widgets will certainly reduce the need to allocate new memory. However, at some point, you will fill up all the available space by simply storing the message objects.



If this project is a real application and not just a quick skectch, you need to improve the memory usage pattern by allocating a certain amount of message heap and manually save / load to the save layer depending on user interaction, you also need a pageview, i.e. a renderer. which has the concept of batch rendering and handles transitions between successive pages smoothly.

0


source







All Articles