Javafx 如何绑定到属性列表

Javafx How to make a Binding to a list of Properties

我想根据宽度随机排列节点数。但是我无法计算宽度的总和(使用它们的属性),我有以下示例代码 - 我没有得到有关其中一个属性更改的通知:

@Override
public void start(Stage arg0) throws Exception {
    List<SimpleIntegerProperty> l = IntStream.range(0, 10)
            .mapToObj(SimpleIntegerProperty::new)
            .collect(Collectors.toList());
    ObservableList<IntegerProperty> widthAr = FXCollections.observableArrayList();
    widthAr.addAll(l);

    IntegerBinding nextheight = Bindings.createIntegerBinding(() -> widthAr.stream()
            .mapToInt(IntegerProperty::get)
            .sum(), widthAr);

    nextheight.addListener((v, o, n) -> System.out.println("New value: " + v.getValue()));

    //Now change randomly one of the IntegerProperties 
    ScheduledExecutorService tfsqueryScheduler = Executors.newScheduledThreadPool(1);

    tfsqueryScheduler.scheduleAtFixedRate(() -> {
        System.out.println("Changing");
        int i = (int) Math.round(Math.random() * 9.4);
        SimpleIntegerProperty v = l.get(i);
        v.set(0);
    }, 0, 3, TimeUnit.SECONDS);

    System.out.println("Start...");
}

nextheight.addListener 从未被调用 :( ...有什么想法吗? 谢谢!

默认情况下,ObservableLists 仅在列表结构发生变化时触发更新(例如,项目被添加到列表或从列表中删除),如果列表中单个元素的状态发生变化则不会。要创建一个在属于其任何元素的属性更改时触发通知的列表,您需要使用 extractor.

创建列表

在这种情况下,您感兴趣的 属性 只是列表元素本身,因此您需要替换

ObservableList<IntegerProperty> widthAr = FXCollections.observableArrayList();

ObservableList<IntegerProperty> widthAr = 
    FXCollections.observableArrayList(w -> new Observable[] {w});

另请注意,根据您的实际用例,您可能需要通过将其设为字段而不是局部变量来确保您的绑定不会过早被垃圾回收。

在此处创建 IntegerProperty 时:

Bindings.createIntegerBinding(() -> widthAr.stream()
        .mapToInt(IntegerProperty::get)
        .sum(), widthAr);

您还需要添加 widthAr 中的所有元素作为依赖项,因为列表不会通知它的元素更改,只有在添加或删除元素时才会通知。

注意:如果从列表中删除或添加元素,这将不起作用,但您不这样做。

这是我在项目中使用的示例。该模型是关于一个包含配方列表的组,每个配方都有一个包含名称和数量的材料列表。这只包含结构,没有加载任何价格。

价格来自外部试剂清单。所以首先我必须将所有试剂映射到每个 material.

    grupos.stream().map(Grupo::getRecetas).flatMap(List::stream).map(Receta::getObsMateriales).flatMap(List::stream)
            .forEach(material -> material.setReagent(buscarReagent(material.getNombre())));

接下来,在所有价格都加载到 material 的 reagentProperty 之后。是时候创建一个绑定,将所有 material* 金额的成本相加。我还在流式传输时设置了配方的 reagentProperty,但它与总和无关。由于如果源试剂更新它的价格,所有事情都是通过绑定完成的,所以这个总和应该动态更新。

grupos.stream().map(Grupo::getRecetas).flatMap(List::stream).forEach(receta -> {
    receta.setReagent(buscarReagent(receta.getNombre()));//doesn't affect the sum binding
    receta.costoProduccionProperty()
            .bind(Bindings.createLongBinding(
                    () -> receta.getObsMateriales().stream().mapToLong(m -> m.getReagent().getValue()*m.getCantidad()).sum(),
                    receta.getObsMateriales()));
});