java 带有自定义对象的 JavaFX 自定义单元工厂

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34838341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 23:25:54  来源:igfitidea点击:

JavaFX custom cell factory with custom Objects

javalistviewjavafxcustom-cell

提问by Mackovich

I am trying to have a custom ListViewmade of custom Cellbased on a list of custom objects.

我正在尝试ListView根据 customCell列表进行自定义objects

The custom object is class name called Messagewhich contains a few fields for the message content, recipient, timestampand status(read, sent etc.).

自定义对象被称为类名Message,其中包含消息内容收件人时间戳状态(读取、发送等)的几个字段。

After looking at this question : Customize ListView in JavaFX with FXMLI have successfully :

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

  1. created a ListView with custom cellswhere the cell design is defined in a FXML file;
  2. associated a controllerso that each cell data can be filled with the current item of the collection ;
  1. 创建了一个带有自定义单元格的 ListView,其中单元格设计在FXML 文件中定义;
  2. 关联一个控制器,以便每个单元格数据都可以填充集合的当前项目;

However, I failed to link both : I cannot seem to find a way so that the current item of the ListView is sent the Cell Controller.

但是,我未能将两者联系起来:我似乎无法找到一种方法来将 ListView 的当前项目发送到Cell Controller

Here is my code for the cell factory and the ListView filling of items:

这是我的单元工厂代码和项目的 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();
    }
});

And now, the ConversationCell class :

现在,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());
    }
}

I cannot show the ConversationCellController but all I can say, this is where (in its constructor) I load the FXML file that designs the cell and then I can fill the values with the given Message item.

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

The getView()method returns the root panethat contains the now-filled-and-designed cell.

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

As I previously say, the designing work, but I cannot seem to link the ListView items with the CellFactorybecause in method

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

protected void updateItem(Message item, boolean empty)

protected void updateItem(消息项,布尔值为空)

emptyis set to trueand item is indeed null.

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

What can I do to make this work ?

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

回答by James_D

All custom cell implementations that override updateItem(...)need to deal with the case where the cell is empty in that method. So you could do a na?ve fix of this with

所有覆盖的自定义单元格实现都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());
        }
    }
}

However, this is not a good solution from the point of view of performance. You are loading the FXML every time updateItem(...)is called with a non-empty cell, and that's a pretty expensive operation (potentially involving file i/o, unzipping the FXML file from a jar file, parsing the file, lots of reflection, creating new UI elements, etc). You don't want to be asking the FX Application Thread to be doing all that work every time the user scrolls the list view by a few pixels. Instead, your cell should cache the node and should update it in the updateItemmethod:

但是,从性能的角度来看,这并不是一个好的解决方案。每次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);
        }
    }
}

You should define a setItem(...)method in the ConversationCellControllerthat updates the view (sets text on labels, etc etc) accordingly.

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