Javafx更新了对象属性上的ListCell

时间:2014-10-31 15:02:48

标签: java listview javafx

我尝试根据可以在另一个面板中编辑的字段来更新列表单元格的样式类。

我尝试了以下操作,但在编辑了文件后,它改变了样式,但它也改变了列表中其他ListCells的样式。

private ListView<ProviderProduct> importProductsListView  = //Set items in list
importProductsListView.setCellFactory(
    new Callback<ListView<ProviderProduct>, ListCell<ProviderProduct>>() {
        public ListCell<ProviderProduct> call(ListView<ProviderProduct> param) {
            final ListCell<ProviderProduct> cell = new ListCell<ProviderProduct>(){

                public void updateItem(ProviderProduct providerProduct, boolean empty){
                    super.updateItem(providerProduct, empty);

                        if(!empty) {
                            setText(providerProduct.toString());

                            if(providerProduct.hasPriceWarning()){
                                getStyleClass().add(Consts.CSS_ALERT);
                            }else{
                                getStyleClass().remove(Consts.CSS_ALERT);
                            }

                            providerProduct.priceListinoProperty().addListener(
                                new ChangeListener<BigDecimal>() {
                                    @Override
                                    public void changed(ObservableValue<? extends BigDecimal> observable,BigDecimal oldValue, BigDecimal newValue) {

                                        if(providerProduct.hasPriceWarning()){
                                            if(!getStyleClass().contains(Consts.CSS_ALERT)){
                                                getStyleClass().add(Consts.CSS_ALERT);
                                            }
                                        }else{
                                            getStyleClass().removeAll(Collections.singleton(Consts.CSS_ALERT));
                                        }
                                    }
                                });
                        }else{ 
                            setText("");
                            getStyleClass().remove(Consts.CSS_ALERT);
                        }
                   }
            };
            return cell;
        }
});

1 个答案:

答案 0 :(得分:1)

我可以看到您的代码存在两个问题:

首先,styleClassList表示,当然可以包含重复的条目。因此,如果您不幸的是将单元格从一个带有警告的项目重用到另一个带有警告的项目,那么最终将添加两次样式类。 remove(...)方法只删除一个副本,因此当您滚动列表时,您将开始看到不一致的行为。

如果您遇到JavaFX 2.2,则需要执行以下操作:

if(providerProduct.hasPriceWarning() & ! getStyleClass().contains(Consts.CSS_ALERT){
    getStyleClass().add(Consts.CSS_ALERT);
}else{
    getStyleClass().remove(Consts.CSS_ALERT);
}

如果你真的想要防弹,那么确保你删除所有事件的方法是使用以下代码来代替你对remove(...)的号召:

getStyleClass().removeAll(Collections.singleton(CSS_ALERT));

如果您可以使用JavaFX 8(即Java 8),您应该考虑使用`PseudoClass',这样更容易,并且据称效率更高。 (另外,使用lambdas代替所有匿名内部类将使您的代码更易于管理。)

其次,每当更新单元格时,都会在项目上注册具有相应属性的侦听器。因此,当单元格被重用以表示新项目时(例如,当用户滚动列表时),它将从多个项目中侦听属性。您需要安排根据需要删除侦听器。

我更喜欢通过创建默认ListCell并观察其itemProperty()来解决此问题,因为这样可以让您在旧项目以及更改旧项目时获得新项目的清晰访问权限。所以你可以这样做:

importProductsListView.setCellFactory(
    new Callback<ListView<ProviderProduct>, ListCell<ProviderProduct>>() {
        public ListCell<ProviderProduct> call(ListView<ProviderProduct> param) {
            final ListCell<ProviderProduct> cell = new ListCell<ProviderProduct>();

            final ChangeListener<BigDecimal> priceListener = new ChangeListener<BigDecimal>() {
                @Override
                public void changed(ObservableValue<? extends BigDecimal> observable,BigDecimal oldValue, BigDecimal newValue) {

                     if(providerProduct.hasPriceWarning()){
                         if(!getStyleClass().contains(Consts.CSS_ALERT)){
                              getStyleClass().add(Consts.CSS_ALERT);
                         }
                     }else{
                         getStyleClass().removeAll(Collections.singleton(Consts.CSS_ALERT));
                     }
                }
            });

            cell.itemProperty().addListener(new ChangeListener<ProviderProduct>() {
                @Override
                public void changed(ObservableValue<? extends ProviderProduct> obs, ProviderProduct oldProduct, ProviderProduct newProduct) {
                    if (oldProduct != null) {
                        oldProduct.priceListinoProperty().removeListener(priceListener);
                    }
                    if (newProduct == null) {
                        cell.setText(null);
                        cell.getStyleClass().removeAll(Collections.singleton(Consts.CSS_ALERT);
                    } else {
                        cell.setText(newProduct.toString());
                        if (newProduct.hasPriceWarning()) {
                            if (! cell.getStyleClass().contains(Consts.CSS_ALERT)) {
                                cell.getStyleClass().add(Consts.CSS_ALERT);
                            }
                        } else {
                            cell.getStyleClass().removeAll(Collections.singleton(Consts.CSS_ALERT));
                        }
                        newProduct.priceListinoProperty().addListener(priceListener);
                    }
                }
            });

            return cell ;
         }
});