JavaFX:如何绑定列表中的多个属性?

时间:2013-06-04 13:15:46

标签: java javafx-2

我有一个类SimpleElement,它有一个权重字段,第二个有一个SimpleElement列表和一个权重字段,它取决于所有其他SimpleElement s的权重之和包含在列表中。任何人都知道怎么做绑定?

我的代码:

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

public class SimpleElement {

    IntegerProperty weight;

    public SimpleElement() {
        weight = new SimpleIntegerProperty();
    }

    public int getWeight() {
        return weight.get();
    }

    public void setWeight(int weight) {
        this.weight.set(weight);
    }

    public IntegerProperty weightProperty() {
        return weight;
    }
}

import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

public class RootElement {

    List<SimpleElement> elements;
    IntegerProperty weight;

    public RootElement() {
        elements = new ArrayList<>();
        weight = new SimpleIntegerProperty();
    }

    public void addelements(SimpleElement element) {
        elements.add(element);
    }
}

3 个答案:

答案 0 :(得分:1)

可以重写RootElement类以创建对每个SimpleElement的绑定,并将它们相加:

public class RootElement {
    List<SimpleElement> elements;
    IntegerProperty weight;
    NumberBinding binding;

    public RootElement() {
        elements = new ArrayList<>();
        weight = new SimpleIntegerProperty();
    }

    public void addelements(SimpleElement element) {
        elements.add(element);

        if (binding == null) {
            binding = element.weightProperty().add(0);
        } else {
            binding = binding.add(element.weightProperty());
        }

        weight.bind(binding);
    }

    public Integer getWeight() {
        return weight.get();
    }

    public ReadOnlyIntegerProperty weightProperty() {
        return weight;
    }
}

使用示例:

public static void main(String[] args) {
    SimpleElement se1 = new SimpleElement();
    SimpleElement se2 = new SimpleElement();
    SimpleElement se3 = new SimpleElement();
    RootElement root = new RootElement();

    root.addelements(se1);
    root.addelements(se2);
    root.addelements(se3);

    root.weightProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            System.out.println(newValue);
        }
    });

    se1.setWeight(3);
    se2.setWeight(2);
    se1.setWeight(4);
}

执行上面的代码会产生:

3

5

6

答案 1 :(得分:1)

Crferreira的答案使用Fluent API来构建绑定链,我发现在删除或替换对象时很难清理和维护。使用低级API会更好。

虽然JavaFX API中有大量预先构建的绑定内容,但当其中一个元素获取新的属性值时,ListBinding将不会失效。因此,您必须创建IntegerBinding子类,该子类侦听列表中的更改并重新绑定到新属性本身。

基于类似answer的代码。

import java.util.ArrayList;
import java.util.List;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

class RootElement {

    ObservableList<SimpleElement> elements = FXCollections.observableList(new ArrayList<SimpleElement>());

    IntegerBinding totalWeight;

    public RootElement() {

        totalWeight = new SumOfWeightsForListOfSimpleElementsIntegerBinding(elements);

    }

    public void addElement(SimpleElement element) {

        elements.add(element);

    }

    public void removeElement(SimpleElement element) {

        elements.remove(element);

    }

    public Integer getWeigth() {
        return totalWeight.getValue();
    }

}

class SimpleElement {

    IntegerProperty weight;

    public SimpleElement() {
        this(0);
    }

    public SimpleElement(Integer weight) {
        this.weight = new SimpleIntegerProperty(weight);
    }

    public int getWeight() {
        return weight.get();
    }

    public void setWeight(int weight) {
        this.weight.set(weight);
    }

    public IntegerProperty weightProperty() {
        return weight;
    }

}

class SumOfWeightsForListOfSimpleElementsIntegerBinding extends IntegerBinding {

    // Reference to our observable list 
    private final ObservableList<SimpleElement> boundList;

    // Array of currently observed properties of elements of our list
    private IntegerProperty[] observedProperties = {};

    // Listener that has to call rebinding in response of any change in observable list
    private final ListChangeListener<SimpleElement> BOUND_LIST_CHANGE_LISTENER
            = (ListChangeListener.Change<? extends SimpleElement> change) -> {
                refreshBinding();
            };

    SumOfWeightsForListOfSimpleElementsIntegerBinding(ObservableList<SimpleElement> boundList) {
        this.boundList = boundList;
        boundList.addListener(BOUND_LIST_CHANGE_LISTENER);
        refreshBinding();
    }

    @Override
    protected int computeValue() {
        int i = 0;
        for (IntegerProperty bp : observedProperties) {
            i += bp.get();
        }

        return i;
    }

    @Override
    public void dispose() {
        boundList.removeListener(BOUND_LIST_CHANGE_LISTENER);
        unbind(observedProperties);
    }

    private void refreshBinding() {
        // Clean old properties from IntegerBinding's inner listener
        unbind(observedProperties);

        // Load new properties    
        List<IntegerProperty> tmplist = new ArrayList<>();
        boundList.stream().map((boundList1) -> boundList1.weightProperty()).forEach((integerProperty) -> {
            tmplist.add(integerProperty);
        });

        observedProperties = tmplist.toArray(new IntegerProperty[0]);

        // Bind IntegerBinding's inner listener to all new properties
        super.bind(observedProperties);

        // Invalidate binding to generate events
        // Eager/Lazy recalc depends on type of listeners attached to this instance
        // see IntegerBinding sources
        this.invalidate();
    }
}

public class Main {

    public static void main(String[] args) {
        SimpleElement se1 = new SimpleElement(10);
        SimpleElement se2 = new SimpleElement(20);
        SimpleElement se3 = new SimpleElement(30);
        RootElement root = new RootElement();

        root.totalWeight.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
            System.out.println(newValue);
        });

        root.addElement(se1);
        root.addElement(se2);
        root.addElement(se3);

        se1.setWeight(1000);
        root.removeElement(se3);

    }

}

令人遗憾的是,监视列表中元素属性总和这样的常见任务需要这个丑陋的样板。

答案 2 :(得分:1)

仅获取总和并将属性绑定到总和。因此,对总和的任何更改都将通过绑定属性来观察