JavaFX自定义列表单元格,updateItem被调用很多

时间:2014-08-11 15:07:01

标签: listview javafx

我在JavaFX应用程序中使用ListView。列表中的项目需要的不仅仅是一个字符串才能显示它们,因此我制作了ListCell<T>的自定义实现,其中T是我正在显示的对象的类。在此自定义类中,我在构造函数中创建BorderPane并覆盖updateItem(T item, boolean empty)。我的想法是,当调用updateItem时,我将单元格的图形设置为BorderPane,同时更改其中Label的一些属性。但是,我注意到updateItem未被调用一次(例如,当通过滚动回收单元格或添加新项目时),但它始终被称为 ,例如当焦点从一个单元格变为另一个单元格时(即使没有滚动),或者调整场景大小,或者按下borderpane中的按钮时,......

我需要ListView使用自定义ListCell。我想在重用单元格时接收一个回调,传递新项目作为参数呈现。然后我想使用该项作为模型来构造一个视图 - 控制器对,我将其视图并将其用作单元格的graphic。此视图中的按钮和其他控件应该可以工作。这在JavaFX中是否可行?

链接问题:

2 个答案:

答案 0 :(得分:9)

基本思想是很少构建单元格,但updateItem(...)方法可能会被频繁调用。没有实际保证该项目在updateItem(...)的调用之间确实发生了变化。 updateItem(...)的默认实现负责处理选择,焦点等,因此如果任何属性发生更改(即使项目未更改),则可能需要调用此方法。

因此,您应该努力减少updateItem(...)方法的开销。目前尚不清楚多次频繁的通话会阻止您做您想做的事情(例如,当您将新项目作为参数传递给您的模型时,请检查它是否与您的模型完全不同你在做任何更新之前已经有了。)

我认为updateItem(...)确实有些错误名称:它不仅在item更新时被调用,而且在任何时候都需要更新单元格。已经有一种机制只有在item发生变化时才能执行代码:只需向单元格itemProperty()注册一个监听器。您可以使用此技术(我通常更喜欢)来创建不同样式的ListCell

ListView<...> listView = ... ;
listView.setCellFactory(lv -> {
    BorderPane cellRoot = new BorderPane();
    // create nodes, register listeners on them, populate cellRoot, etc...
    ListCell<...> cell = new ListCell<>();
    cell.itemProperty().addListener((obs, oldItem, newItem) -> {
        if (newItem != null) {
            // update cellRoot (or its child nodes' properties) accordingly
        }
    });
    cell.emptyProperty().addListener((obs, wasEmpty, isEmpty) -> {
        if (isEmpty) {
            cell.setGraphic(null);
        } else {
            cell.setGraphic(cellRoot);
        }
    });
    cell.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    return cell ;
});

这种方法在您的方案中可能会更好。

答案 1 :(得分:0)

我刚刚开始使用JavaFX而且我无法解释其工作原理的所有细节(它在新项目和现有项目上调用方法),但我解决问题的方法是简单地调用myContainer.getChildren().removeAll( ... )

在我的ListCell public class MyCell extends ListCell<MyContent>的实现中,我创建了许多不同的项目并将它们添加到容器中,该容器在我的列表视图中成为一行。所以,在我将它们添加回来之前,我只需要从该容器中删除相同的项目。这是我所做的简化版本。似乎工作得很好。我确实尝试过James_D的建议,但它对我不起作用并产生同样的错误。

@Override
    public void updateItem(MyContent item, boolean empty) {

        super.updateItem(item, empty);

        if (item == null || empty == true) {
            setGraphic(null);
            setText(null);
        } else {    

            // here I user the "item" to decide how to create 
            // objects a, b, c ... which are Rectangle, Label and Label
            // I also update myMainContainer, which is a Pane

            myMainContainer.getChildren().removeAll(a, b, c);
            myMainContainer.getChildren().addAll(a, b, c);
            setGraphic(myMainContainer);
        }
    }