ListView中单元格的奇怪循环-JavaFX

ListView中单元格的奇怪循环-JavaFX,listview,javafx-2,Listview,Javafx 2,我从JavaFX的文档中了解到,当没有足够的单元格填充ListView(或类似列表)中提供的区域时,会生成新的单元格。否则,通过对具有相应项的每个单元格调用updateItem方法,可以重用现有单元格 我正在实现一个聊天应用程序,在这个程序中,用户向文本区域写入一些内容,按enter键,聊天被附加到文本区域上方的历史记录中,非常简单和经典。历史记录是一个列表视图,显示迄今为止发送/接收的聊天列表。因此,当用户按下enter键时,我将一个新项添加到基础的可观察列表中,这样它就会显示在ListVie

我从JavaFX的文档中了解到,当没有足够的单元格填充ListView(或类似列表)中提供的区域时,会生成新的单元格。否则,通过对具有相应项的每个单元格调用updateItem方法,可以重用现有单元格

我正在实现一个聊天应用程序,在这个程序中,用户向文本区域写入一些内容,按enter键,聊天被附加到文本区域上方的历史记录中,非常简单和经典。历史记录是一个列表视图,显示迄今为止发送/接收的聊天列表。因此,当用户按下enter键时,我将一个新项添加到基础的可观察列表中,这样它就会显示在ListView中

问题是,当一个新的聊天添加到ListView时,列表中会出现某种闪烁,更新列表中的项目需要一点时间但是,新添加的聊天在列表的末尾,用户看不到它(现在需要向下滚动)。因此,实际上不需要更新已经可见的单元格,但确实需要更新。我试图简化updateItem方法的内容,但没有办法。。。它总是在眨眼

然后我实现了一个简单的类,如

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;
    }
}
然后通过将ID分配给生成的单元格

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);
        ....
    }
}
公共类通信Titycell扩展ListCell{
...
private int id=IdGenerator.getInstance().getId();
....
@凌驾
受保护的同步void updateItem(通信实体,布尔空){
super.updateItem(实体,空);
系统输出打印项次(id);
....
}
}
我观察到,当一个新项目被添加到基础列表中时,单元格被复制,而不是回收。(无论如何,这可能就是眨眼的原因)


细胞复制是否正常/合乎逻辑/预期?这是一个(已知的)错误吗?你见过这个吗?任何解决方法都将不胜感激。

您的应用程序似乎不需要
ListView
API
ListView
不仅仅用于在垂直区域中显示小部件,还用于将集合绑定到小部件。在您的情况下,我只需使用包装在
滚动窗格中的
VBox
,并在聊天系统确认新的孩子后立即添加他们

在评论中,您明确表示您选择了
ListView
API来提高内存使用率。当然,存储固定数量的小部件可以减少分配新内存的需要。不过,在某些时候,您只需存储消息对象就可以填满所有可用空间


如果这个项目是一个真正的应用程序,而不仅仅是一个快速的sketcch,那么您需要通过将一定数量的堆专用于消息来改进内存使用模式,并根据用户交互手动存储/加载到持久层。您还需要一个可分页的视图,即具有渲染批处理概念的渲染设备,可以平滑地处理连续页面之间的转换。

我创建了一个类似的测试:

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);
        }

    }

}
导入javafx.application.application;
导入javafx.event.ActionEvent;
导入javafx.event.EventHandler;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.ListCell;
导入javafx.scene.control.ListView;
导入javafx.scene.layout.BorderPane;
导入javafx.stage.stage;
导入javafx.util.Callback;
公共类ListCellReuseTest扩展了应用程序{
@凌驾
公共无效开始(阶段primaryStage){
最终边界窗格根=新边界窗格();
最终ListView列表=新建ListView();
最终按钮addButton=新按钮(“添加消息”);
addButton.setOnAction(新的EventHandler(){
@凌驾
公共无效句柄(ActionEvent事件){
int count=list.getItems().size()+1;
System.out.println(“创建消息”+计数);
list.getItems().add(“消息”+计数);
}
});
list.setCellFactory(新回调(){
@凌驾
公共ListCell调用(ListView ListView){
返回新的TestListCell();
}
});
root.setCenter(列表);
root.setBottom(addButtom);
原始阶段。设置场景(新场景(根,200400));
primaryStage.show();
}
公共静态void main(字符串[]args){
发射(args);
}
公共静态类TestListCell扩展了ListCell{
私有静态int nextId;
私有最终int id;
公共TestListCell(){
id=++nextId;
}
@凌驾
public void updateItem(字符串项,布尔值为空){
super.updateItem(项,空);
setText(项目);
系统输出打印项次(id);
}
}
}
在JavaFX2.2中,这似乎创建了大量的ListCell(每次我按下按钮时有17个新的单元格;有16个单元格可见),并且调用updateItem(…)的次数甚至更多。我猜这些单元中的许多很快就超出了范围,被垃圾收集

在JavaFX8下运行相同的代码显示出完全不同的行为;它会在第一次按下按钮时创建17个单元格,然后再也不会创建新的单元格。对updateItem(…)的调用要少得多,而且在不显示新单元格时,也没有调用。这看起来应该更有效,更直观,更符合我的预期

但是,我没有看到您在JavaFX2.2或JavaFX8下报告的任何“闪烁”;因此,目前还不清楚这是否与单元创建和重用行为直接相关。可能是updateItem(…)方法的其余部分效率更高(例如,缓存复杂的图形,而不是每次都重新创建它们),或者是由于其他原因造成的

在我以前的sim卡中,您在JavaFX8下看到了相同的内容吗?