我在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
答案 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。当现有节点的属性/渲染发生变化时,它会自动更新。所以你只需要更新现有节点(单元格)的外观/属性。不要忘记,在滚动/重新渲染等过程中,可以大量创建单元格。