This sounds to me that you might benefit from reconsidering your object model. It seems that your Product
has two different values you refer to as amount
: one that's displayed in table1, and one that's displayed in table2. If you make those two different fields of the Product
class, then all this becomes much easier, as you can represent the items in both tables with the same actual objects (just with different properties displayed in the columns); and then the criteria for disabling a row becomes just a function of the object you're looking at (along with table2.getItems().contains(...)
).
Another option might be an SKU
class:
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...
}
Then both tables can be TableView<SKU>
s with the cell value factories on the columns just looking at the properties from the correct product. That way row.getItem()
in table1's row factory returns the SKU
object, and can look at the table2 properties as needed.
If you really can't do that, then one way to do this would be to maintain an ObservableMap<String, Double>
which maps SKUs to the amount displayed in table2. This is a bit of work, but not too bad:
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()));
Now you need to keep the map updated when the table contents change:
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()));
}
}
});
And then the disable criteria can be something like
row.disableProperty().bind(Bindings.createBooleanBinding(
() -> row.getItem() != null &&
skuAmountLookup.containsKey(row.getItem().getSKU()) &&
skuAmountLookup.get(row.getItem().getSKU()) < 0,
skuAmountLookup,
row.itemProperty()));
I haven't tried any of this, but it should work. There are two assumptions here:
- SKUs are unique in table 2
- The amount in table 2 isn't going to change, other than by items being added or removed from the table. This can be worked around if needed, but it adds one more layer of complexity to the listener on table2's items.
table1
andtable2
are bothTableView<Product>
s. So isn't the amount of the product in table1 necessarily the same as the amount of the product in table2 that has the same SKU (aren't they the same object..)? – Epigastrium