【问题标题】:JavaFX custom cell factory with custom Objects带有自定义对象的 JavaFX 自定义单元工厂
【发布时间】:2016-04-22 15:45:47
【问题描述】:

我正在尝试根据自定义objects 列表由自定义Cell 制成自定义ListView

自定义对象是名为Message的类名,其中包含消息contentrecipienttimestamp的几个字段>状态(读取、发送等)。

看了这个问题后:Customize ListView in JavaFX with FXML我成功了:

  1. 创建了一个带有自定义单元格的 ListView,其中单元格设计在 FXML 文件中定义;
  2. 关联了一个控制器,这样每个单元格的数据就可以用集合的当前项来填充;

但是,我未能链接两者:我似乎无法找到将 ListView 的当前项发送到 Cell Controller 的方法。

这是我的单元工厂代码和项目的 ListView 填充:

final ObservableList observableList = FXCollections.observableArrayList();
observableList.setAll(myMessages); //assume myMessage is a ArrayList<Message>
conversation.setItems(observableList); //the listview
conversation.setCellFactory(new Callback<ListView<Message>, ListCell<Message>>() {
    @Override
    public ConversationCell<Message> call(ListView<Message> listView) {
        return new ConversationCell();
    }
});

现在,ConversationCell 类:

public final class ConversationCell<Message> extends ListCell<Message> { 

    @Override
    protected void updateItem(Message item, boolean empty) {
        super.updateItem(item, empty);
        ConversationCellController ccc = new ConversationCellController(null);
        setGraphic(ccc.getView());
    }
}

我无法显示 ConversationCellController 但我只能说,这是(在其构造函数中)我加载设计单元格的 FXML 文件的位置,然后我可以使用给定的消息项填充值。

getView() 方法返回包含现在填充和设计的单元格的根窗格

正如我之前所说,设计工作,但我似乎无法将 ListView 项目与 CellFactory 链接,因为在方法中

protected void updateItem(Message item, boolean empty)

empty 设置为 true 并且 item 确实是 null

我能做些什么来完成这项工作?

【问题讨论】:

    标签: java listview javafx custom-cell


    【解决方案1】:

    所有覆盖updateItem(...) 的自定义单元格实现都需要处理该方法中单元格为空的情况。所以你可以用

    来简单地解决这个问题
    public final class ConversationCell<Message> extends ListCell<Message> { 
    
        @Override
        protected void updateItem(Message item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setGraphic(null);
            } else {
                // did you mean to pass null here, or item??
                ConversationCellController ccc = new ConversationCellController(null);
                setGraphic(ccc.getView());
            }
        }
    }
    

    但是,从性能的角度来看,这并不是一个好的解决方案。每次使用非空单元格调用 updateItem(...) 时,您都在加载 FXML,这是一个非常昂贵的操作(可能涉及文件 i/o、从 jar 文件中解压缩 FXML 文件、解析文件、大量反射,创建新的 UI 元素等)。您不希望每次用户将列表视图滚动几个像素时都要求 FX 应用程序线程执行所有这些工作。相反,您的单元应缓存该节点并应在 updateItem 方法中更新它:

    public final class ConversationCell<Message> extends ListCell<Message> { 
    
        private final ConversationCellController ccc = new ConversationCellController(null);
        private final Node view = ccc.getView();
    
        @Override
        protected void updateItem(Message item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setGraphic(null);
            } else {
                ccc.setItem(item);
                setGraphic(view);
            }
        }
    }
    

    您应该在ConversationCellController 中定义一个setItem(...) 方法来相应地更新视图(在标签上设置文本等)。

    【讨论】:

    • 嘿,这其实是一个非常好的观点!就像带有 ViewHolder 模式的 Android ListView 适配器一样,可以跟踪已经生成的视图以减少 CPU 使用率和延迟。我会尽快尝试你的想法。你能否告诉我我可以用我当前的设置/代码做什么来触发事件,以便更新特定的单元格?例如,我有一个正在发送消息的进度条,我想更新它的进度。谢谢!
    • 对于更新,这是一个不同的问题,您可以通过使用 Message 类中的 JavaFX 属性并使用提取器创建项目列表来安排列表视图更新其单元格。见stackoverflow.com/questions/23822550
    • 谢谢。我会看看的 !顺便说一句,我实施了您的解决方案,并且效果很好。我的 ObservableList 中没有很多项目(只有几个),但我看起来更活泼!无论如何,我还注意到ccc.setItem 被多次调用,所以我不得不在ConversationCellController 中添加一个布尔值以避免将视图设置为多次。我这样做对吗?现在它可以工作,但我想知道......谢谢!
    猜你喜欢
    • 1970-01-01
    • 2013-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-25
    • 2021-04-15
    • 1970-01-01
    相关资源
    最近更新 更多