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

sco*_*ttb 8 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); }
    }};
}
Run Code Online (Sandbox Code Playgroud)

我有一个名为"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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

Eld*_*ell 13

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

public void refresh() {
    super.flow.recreateCells(); 
}
Run Code Online (Sandbox Code Playgroud)

这将调用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    
Run Code Online (Sandbox Code Playgroud)

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


And*_*rey 12

对于最终在此页面上的任何其他身体:

这样做的正确方法是为您的可观察列表提供一个"提取器"回调.这将通知列表(和ListView)任何属性更改.

要显示的自定义类:

public class Custom {
    StringProperty name = new SimpleStringProperty();
    IntegerProperty id = new SimpleIntegerProperty();

    public static Callback<Custom, Observable[]> extractor() {
        return new Callback<Custom, Observable[]>() {
            @Override
            public Observable[] call(Custom param) {
                return new Observable[]{param.id, param.name};
            }
        };
    }

    @Override
    public String toString() {
        return String.format("%s: %s", name.get(), id.get());
    }
}
Run Code Online (Sandbox Code Playgroud)

而你的主要代码体:

ListView<Custom> myListView;
//...init the ListView appropriately
ObservableList<Custom> items = FXCollections.observableArrayList(Custom.extractor());
myListView.setItems(items);
Custom item = new Custom();
items.add(item);
item.name.set("Mickey Mouse");
// ^ Should update your ListView!!!
Run Code Online (Sandbox Code Playgroud)

请参阅(和类似方法):https: //docs.oracle.com/javafx/2/api/javafx/collections/FXCollections.html#observableArrayList(javafx.util.Callback)


Dom*_*Dom 9

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

ListView<T> list;
list.refresh();
Run Code Online (Sandbox Code Playgroud)


sco*_*ttb 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

帮助器方法只是从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);
}
Run Code Online (Sandbox Code Playgroud)