JavaFX:BooleanBindings与多个EasyBind映射绑定



这个问题更进一步,JavaFX:基于其他TableView停止禁用TableView中的多行。我想提出一个更一般的话题,其他人也可以从中受益。

我还有两个表视图。当表1包含相同的对象时,我还想禁用表2中的一行。这是通过以下代码实现的:

//Check if a row needs to be disabled: this is achieved with a rowfactory
ObservableList<String> listOfSKUs = EasyBind.map(table1.getItems(),Product::getSKU);
table2.setRowFactory(tv -> {
    TableRow<Product> row = new TableRow<>();
    //The row doesnt need to be checked when it is empty, thats the first part
    //The second part is a big OR: the row is a gift card OR the listOfSkus contains the row. In this last one, the amount also needs to be checked.
    row.disableProperty().bind(Bindings.createBooleanBinding( () -> 
        row.getItem() != null && 
        (row.getItem().getProductName().equals("Kadobon") ||
        (listOfSKUs.contains(row.getItem().getSKU()) //&& some part to check for the amount)  
        ), listOfSKUs, row.itemProperty() ));
     return row;
});

现在我只想在以下情况下禁用该行:

  1. 表1中产品的SKU也在表2中(现在可以使用)
  2. 表2中的产品数量为负数

我不知道如何检查金额,因为我需要与特定SKU关联的金额。如何在一个BooleanBinding中创建多个贴图?

非常感谢您的帮助!

在我看来,您可能会从重新考虑对象模型中受益。您的Product似乎有两个不同的值,您称之为amount:一个显示在表1中,另一个则显示在表2中。如果您使Product类的这两个不同字段,那么这一切都会变得容易得多,因为您可以用相同的实际对象表示两个表中的项目(只是在列中显示不同的属性);然后,禁用行的标准就变成了您正在查看的对象的函数(以及table2.getItems().contains(...))。

另一种选择可能是SKU类:

public class SKU {
    private final StringProperty sku = ... ;
    // Obviously use more meaningful names for these:
    private final ObjectProperty<Product> table1Product = ... ;
    private final ObjectProperty<Product> table2Product = ... ;
    // getters, setters etc...
}

然后,这两个表都可以是TableView<SKU>,列上的单元格值工厂只查看正确产品的属性。这样,表1的行工厂中的row.getItem()返回SKU对象,并可以根据需要查看表2的属性。

如果你真的不能做到这一点,那么一种方法就是维护一个ObservableMap<String, Double>,它将SKU映射到表2中显示的数量。这是一项工作,但还不错:

ObservableMap<String, Double> skuAmountLookup = table2.getItems().stream().collect(
    Collectors.toMap(Product::getSKU, Product::getAmount,
    // the next argument is a merge function, which is used if there are two (or more) items
    // in table2 with the same SKU. This function would replace the first value by the second.
    // If the SKUs are unique it doesn't really matter what you have here...
    (x, y) -> y,
    () -> FXCollections.observableHashMap()));

现在,当表格内容发生变化时,您需要保持地图的更新:

table2.getItems().addListener((ListChangeListener.Change<? extends Product> change) -> {
    // can just do skuAmountLookup.clear(); followed by skuAmountLookup.putAll(...)
    // passing in the same data used above to initialize it
    // That's pretty inefficient though, the following will just update what's needed.
    // This assumes SKUs are unique in table 2:
    while (change.next()) {
        if (change.wasAdded()) {
            change.getAddedSubList().forEach(product -> 
                skuAmountLookup.put(product.getSKU(), product.getAmount()));
        }
        if (change.wasRemoved()) {
            change.getRemoved().forEach(product -> skuAmountLookup.remove(product.getSKU()));
        }
    }
});

然后禁用标准可以是类似的东西

row.disableProperty().bind(Bindings.createBooleanBinding(
    () -> row.getItem() != null &&
          skuAmountLookup.containsKey(row.getItem().getSKU()) &&
          skuAmountLookup.get(row.getItem().getSKU()) < 0, 
    skuAmountLookup,
    row.itemProperty()));

我还没试过这些,但应该有效。这里有两个假设:

  1. SKU在表2中是唯一的
  2. 除了表中添加或删除的项目外,表2中的金额不会发生变化。如果需要,这可以解决,但它为表2的项的侦听器增加了一层复杂性

最新更新