JavaFX 2.2:如何强制重写/更新ListView

时间:2013-06-02 05:43:43

标签: java listview javafx-2

我在JavaFX 2模式对话框窗口中有一个ListView控件。

此ListView显示DXAlias实例,其列表单元由单元工厂制造。工厂对象所做的主要工作是检查ListView的UserData属性数据,并将其与ListCell对应的项进行比较。如果它们相同,则ListCell的内容将呈现为红色,否则为黑色。我这样做是为了指示ListView中的哪些项目当前被选为“默认”。这是我的ListCell工厂类,所以你可以看到我的意思:

private class AliasListCellFactory implements 
    Callback<ListView<DXSynonym>, ListCell<DXSynonym>> {

@Override
public ListCell<DXSynonym> call(ListView<DXSynonym> p) {
    return new ListCell<DXSynonym>() {

    @Override
    protected void updateItem(DXSynonym item, boolean empty) {
        super.updateItem(item, empty);

        if (item != null) {
            DXSynonym dx = (DXSynonym) lsvAlias.getUserData();

            if (dx != null && dx == item) {
                this.setStyle("-fx-text-fill: crimson;");                    
            } else { this.setStyle("-fx-text-fill: black;"); }

            this.setText(item.getDxName());

        } else { this.setText(Census.FORMAT_TEXT_NULL); }
    }};
}

我有一个名为“handleAliasDefault()”的按钮处理程序,它通过获取所选的DXAlias实例并将其存储到ListView:lsvAlias.setUserData(选定的DXAlias),使ListView中的选定项成为新的默认值。这是处理程序代码:

// Handler for Button[fx:id="btnAliasDefault"] onAction
    @FXML
    void handleAliasDefault(ActionEvent event) {

        int sel = lsvAlias.getSelectionModel().getSelectedIndex();
        if (sel >= 0 && sel < lsvAlias.getItems().size()) {
            lsvAlias.setUserData(lsvAlias.getItems().get(sel));
        }
    }

由于响应单击“设置默认值”按钮所做的更改是更改ListView的UserData()而不对后备ObservableList进行任何更改,因此列表未正确指示新的默认值。

有没有办法强制ListView重新呈现其ListCells? Android用户就此问题提出了数千万的问题,但似乎没有JavaFX的幸福。我可能不得不对支持数组进行“无意义的改变”以强制重绘。

我看到有人要求JavaFX 2.1:Javafx ListView refreshing

4 个答案:

答案 0 :(得分:12)

不确定这是否适用于JavaFX 2.2,但它确实在JavaFX 8中有用,并且花了一些时间才弄明白。您需要创建自己的ListViewSkin并添加refresh方法,例如:

public void refresh() {
    super.flow.recreateCells(); 
}

这将调用updateItem而无需替换整个Observable集合。

此外,要使用新的Skin,您需要实例化它并在控制器的initialize方法上进行设置,以防您使用FXML:

MySkin<Subscription> skin = new MySkin<>(this.listView); // Injected by FXML
this.listView.setSkin(skin);
...
((MySkin) listView.getSkin()).refresh(); // This is how you use it    

我在调试手风琴的行为后发现了这一点。每次展开时,此控件都会刷新它包含的ListView。

答案 1 :(得分:5)

现在,我能够通过在我的按钮处理程序中使用以下方法(重命名为forceListRefreshOn())来重绘ListView并正确指示所选的默认值:

@FXML
void handleAliasDefault(ActionEvent event) {

    int sel = lsvAlias.getSelectionModel().getSelectedIndex();
    if (sel >= 0 && sel < lsvAlias.getItems().size()) {
        lsvAlias.setUserData(lsvAlias.getItems().get(sel));
        this.<DXSynonym>forceListRefreshOn(lsvAlias);
    }
}

帮助器方法只是从ListView中交换出ObservableList,然后将其交换回来,可能是强制ListView更新其ListCells:

private <T> void forceListRefreshOn(ListView<T> lsv) {
    ObservableList<T> items = lsv.<T>getItems();
    lsv.<T>setItems(null);
    lsv.<T>setItems(items);
}

答案 2 :(得分:2)

我不确定我是否遗漏了一些东西,但起初我尝试过scottb的解决方案,但后来我发现有一个已经实现的refresh()方法,这对我有用。

ListView<T> list;
list.refresh();

答案 3 :(得分:0)

似乎,您不需要使用updateItem方法。您需要的是 - 将当前默认单元格的指示符(现在位于用户数据中)存储到OblectProperty中。并在每个单元格的此属性上添加一个侦听器。当值更改时,重新分配样式。

但是我认为,这样的绑定会调用另一个问题 - 滚动时,会创建新的单元格,但旧的单元格不会离开绑定,这可能会导致内存泄漏。因此,您需要在场景中删除单元格时添加侦听器。我认为,可以通过在parentProperty上添加一个侦听器来实现,当它变为null时 - 删除绑定。

不应强制更新UI。当现有节点的属性/渲染发生变化时,它会自动更新。所以你只需要更新现有节点(单元格)的外观/属性。不要忘记,在滚动/重新渲染等过程中,可以大量创建单元格。