JavaFX开发桌面,移动端,嵌入式权威指南(四)——属性和绑定

  • MotivatingExample
  • 理解关键接口和概念
  • 理解观察接口
  • 理解观测值接口
  • 理解可写入属性接口
  • 理解只读属性接口
  • 创造绑定
  • 理解 Bindings工具类
  • 理解JavaFX Bean约定
  • 将JavaFX Bean适配成JavaFX属性


MotivatingExample

让我们从示例开始,该示例显示简单整数属性类的几个实例。

package sample;

import javafx.beans.InvalidationListener;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class MotivatingExample {
    private static IntegerProperty intProperty;
    public static void main(String[] args) {
        createProperty();
        addAndRemoveInvalidationListener();
        addAndRemoveChangeListener();
        bindAndUnbindOnePropertyToAnother();
    }
    private static void createProperty() {
        intProperty = new SimpleIntegerProperty(1024);
        System.out.println("intProperty = " + intProperty);
        System.out.println("intProperty.get() = " + intProperty.get());
        System.out.println("intProperty.getValue() = " + intProperty.getValue().intValue());
    }

    private static void addAndRemoveInvalidationListener() {
        System.out.println();
        final InvalidationListener invalidationListener = observable ->
                System.out.println("The observable has been invalidated: " + observable + ".");
        intProperty.addListener(invalidationListener);
        System.out.println("Added invalidation listener.");
        System.out.println("Calling intProperty.set(2048).");
        intProperty.set(2048);
        System.out.println("Calling intProperty.setValue(3072).");
        intProperty.setValue(Integer.valueOf(3072));
        intProperty.removeListener(invalidationListener);
        System.out.println("Removed invalidation listener.");
        System.out.println("Calling intProperty.set(4096).");
        intProperty.set(4096);
    }
    private static void addAndRemoveChangeListener() {
        System.out.println();
        final ChangeListener changeListener = ( ObservableValue observableValue, Object oldValue,
                                                Object newValue) ->
                System.out.println( "The observableValue has changed: oldValue = " + oldValue +
                        "newValue = " + newValue);
                        intProperty.addListener(changeListener);
        System.out.println("Added change listener.");
        System.out.println("Calling intProperty.set(5120).");
        intProperty.set(5120);
        intProperty.removeListener(changeListener);
        System.out.println("Removed change listener.");
        System.out.println("Calling intProperty.set(6144).");
        intProperty.set(6144);
    }
    private static void bindAndUnbindOnePropertyToAnother() {
        System.out.println();
        IntegerProperty otherProperty = new SimpleIntegerProperty(0);
        System.out.println("otherProperty.get() = " + otherProperty.get());
        System.out.println("Binding otherProperty to intProperty.");
        otherProperty.bind(intProperty);
        System.out.println("otherProperty.get() = " + otherProperty.get());
        System.out.println("Calling intProperty.set(7168).");
        intProperty.set(7168);
        System.out.println("otherProperty.get() = " + otherProperty.get());

        System.out.println("Unbinding otherProperty from intProperty.");
        otherProperty.unbind();
        System.out.println("otherProperty.get() = " + otherProperty.get());
        System.out.println("Calling intProperty.set(8192).");
        intProperty.set(8192);
        System.out.println("otherProperty.get() = " + otherProperty.get());
    }
}

理解关键接口和概念

UML图显示JavaFX属性类和绑定框架的关键接口及其包含一些接口。

JavaFx fxml 取消登录框 javafx登录界面布局_System

理解观察接口

层次结构的根节点是可观察接口。将监听器对象注册到接收事件的可观察对象。在上述示例中的简单整数属性接口。当set()或setValue()方法被调用将基础值int值改变,上述的绑定的事件会被触发。

理解观测值接口

final ChangeListener changeListener = ( ObservableValue observableValue, Object oldValue,
                                                Object newValue) ->
                System.out.println( "The observableValue has changed: oldValue = " + oldValue +
                        "newValue = " + newValue);

层次结构中的下一个是可观察值接口。它只是一个简单的可观察值。它getValue()方法返回其值。我们在MotivatingExample 调用的SimpleIntegerProperty对象中的getValue()方法可以被认为是来自此接口。您可以注册更改听者对象接收更改事件的可观察值对象。您在最后一节的激励示例中看到更改事件被激发。当更改事件触发时,ChangeListener 收到另外两条信息:可观察值对象的旧值和新值。

理解可写入属性接口

写入属性接口其目的是注入getValue(),并将setValue()方法注入到此接口的实现中。JavaFX 属性和绑定框架中的所有可写值实现类也都实现可观察值接口,因此您可以认为可写入属性接口仅提供setValue()方法。您在MotivatingExample 中看到了setValue()方法。

理解只读属性接口

ReadOnlyProperty 接口为其实现注入了两种方法。getBean()方法应返回如果对象中未包含包含读取仅操作性或无效的对象。getName()方法如果ReadOnlyRroperty没有名称,则应返回""空字符串"的名称。包含对象和名称提供有关ReadOnlyRroperty的上下文信息。上下文属性信息在无效事件的传播或重新计算中不起任何直接作用。但是它将在某些外围计算中予以考虑。在我们的MotivatingExample 中,intProperty 是在没有任何上下文信息的情况下构建的。如果我们使用
完整的构造器,可以提供它的名称。

intProperty = new SimpleIntegerProperty(null, "intProperty", 1024);

the output would have contained the property name:
intProperty = IntegerProperty [name: intProperty, value: 1024]

创造绑定

JavaFX 属性和绑定框架提供了绑定的三种方法

  • 扩展整数绑定系列抽象类。
  • 使用Bindings工具类中的静态绑定创建方法
  • 使用由IntegerExpression系列抽象类提供的流式应用接口。

理解 Bindings工具类

绑定类包含 236 种工厂方法,这些方法利用现有的可观察值和常规值。大多数方法都重载,考虑到可观察值和常规 Java(不可见)值可用于构建新的绑定。其中至少一个参数必须是可观察到的值。
以下是九个add方法的重载方法。
public static NumberBinding add(ObservableNumberValue n1, ObservableNumberValue n2)
public static DoubleBinding add(ObservableNumberValue n, double d)
public static DoubleBinding add(double d, ObservableNumberValue n)
public static NumberBinding add(ObservableNumberValue n, float f)
public static NumberBinding add(float f, ObservableNumberValue n)
public static NumberBinding add(ObservableNumberValue n, long l)
public static NumberBinding add(long l, ObservableNumberValue n)
public static NumberBinding add(ObservableNumberValue n, int i)
public static NumberBinding add(int i, ObservableNumberValue n)
当add()方法被调用时,它会返回一个包含所有可观察到的依赖项绑定值参数对象,其值是其两个参数值的总和。还存在类似的subtract(), multiply(), and divide()方法有类似重载方法。

扩展整数绑定系列抽象类。

package sample;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public class HeronsFormulaDirectExtensionExample {
    public static void main(String[] args) {
        final DoubleProperty a = new SimpleDoubleProperty(0);
        final DoubleProperty b = new SimpleDoubleProperty(0);
        final DoubleProperty c = new SimpleDoubleProperty(0);
        DoubleBinding area = new DoubleBinding() {
            {
                super.bind(a, b, c);
            }
            @Override
            protected double computeValue() {
                double a0 = a.get();
                double b0 = b.get();
                double c0 = c.get();
                if ((a0 + b0 > c0) && (b0 + c0 > a0) && (c0 + a0 > b0)) {
                    double s = (a0 + b0 + c0) / 2.0D;
                    return Math.sqrt(s * (s - a0) * (s - b0) * (s - c0));
                } else {
                    return 0.0D;
                }
            }
        };
        a.set(3);
        b.set(4);
        c.set(5);
        System.out.printf("Given sides a = %1.0f, b = %1.0f, and c = %1.0f," +
                        " the area of the triangle is %3.2f\n", a.get(), b.get(), c.get(),
                area.get());
        a.set(2);
        b.set(2);
        c.set(2);
        System.out.printf("Given sides a = %1.0f, b = %1.0f, and c = %1.0f," +
                        " the area of the triangle is %3.2f\n", a.get(), b.get(), c.get(),
                area.get());
    }
}

使用Bindings工具类中的静态绑定创建方法。
Area = (x1y2 + x2y3 + x3y1 – x1y3 – x2y1 – x3y2) / 2**

package sample;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class TriangleAreaExample {
    public static void main(String[] args) {
        IntegerProperty x1 = new SimpleIntegerProperty(0);
        IntegerProperty y1 = new SimpleIntegerProperty(0);
        IntegerProperty x2 = new SimpleIntegerProperty(0);
        IntegerProperty y2 = new SimpleIntegerProperty(0);
        IntegerProperty x3 = new SimpleIntegerProperty(0);
        IntegerProperty y3 = new SimpleIntegerProperty(0);
        final NumberBinding x1y2 = Bindings.multiply(x1, y2);
        final NumberBinding x2y3 = Bindings.multiply(x2, y3);
        final NumberBinding x3y1 = Bindings.multiply(x3, y1);
        final NumberBinding x1y3 = Bindings.multiply(x1, y3);
        final NumberBinding x2y1 = Bindings.multiply(x2, y1);
        final NumberBinding x3y2 = Bindings.multiply(x3, y2);
        final NumberBinding sum1 = Bindings.add(x1y2, x2y3);
        final NumberBinding sum2 = Bindings.add(sum1, x3y1);
        final NumberBinding sum3 = Bindings.add(sum2, x3y1);
        final NumberBinding diff1 = Bindings.subtract(sum3, x1y3);
        final NumberBinding diff2 = Bindings.subtract(diff1, x2y1);
        final NumberBinding determinant = Bindings.subtract(diff2, x3y2);
        final NumberBinding area = Bindings.divide(determinant, 2.0D);
        x1.set(0); y1.set(0);
        x2.set(6); y2.set(0);
        x3.set(4); y3.set(3);
        printResult(x1, y1, x2, y2, x3, y3, area);
        x1.set(1); y1.set(0);
        x2.set(2); y2.set(2);
        x3.set(0); y3.set(1);
        printResult(x1, y1, x2, y2, x3, y3, area);
    }
    private static void printResult(IntegerProperty x1, IntegerProperty y1,
                                    IntegerProperty x2, IntegerProperty y2,
                                    IntegerProperty x3, IntegerProperty y3,
                                    NumberBinding area) {
        System.out.println("For A(" +
                x1.get() + "," + y1.get() + "), B(" +
                x2.get() + "," + y2.get() + "), C(" +
                x3.get() + "," + y3.get() + "), the area of triangle ABC is " + area.getValue());
    }
}

For A(0,0), B(6,0), C(4,3), the area of triangle ABC is 9.0
For A(1,0), B(2,2), C(0,1), the area of triangle ABC is 1.5

使用由IntegerExpression系列抽象类提供的流式写法。

package sample;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.StringExpression;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

public class TriangleAreaFluentExample {
    public static void main(String[] args) {
        IntegerProperty x1 = new SimpleIntegerProperty(0);
        IntegerProperty y1 = new SimpleIntegerProperty(0);
        IntegerProperty x2 = new SimpleIntegerProperty(0);
        IntegerProperty y2 = new SimpleIntegerProperty(0);
        IntegerProperty x3 = new SimpleIntegerProperty(0);
        IntegerProperty y3 = new SimpleIntegerProperty(0);
        final NumberBinding area = x1.multiply(y2)
                .add(x2.multiply(y3))
                .add(x3.multiply(y1))
                .subtract(x1.multiply(y3))
                .subtract(x2.multiply(y1))
                .subtract(x3.multiply(y2))
                .divide(2.0D);
        StringExpression output = Bindings.format(
                "For A(%d,%d), B(%d,%d), C(%d,%d), the area of triangle ABC is %3.1f",
                x1, y1, x2, y2, x3, y3, area);
        x1.set(0); y1.set(0);
        x2.set(6); y2.set(0);
        x3.set(4); y3.set(3);
        System.out.println(output.get());
        x1.set(1); y1.set(0);
        x2.set(2); y2.set(2);
        x3.set(0); y3.set(1);
        System.out.println(output.get());
    }
}

理解JavaFX Bean约定

JavaFX 引入了 JavaFX Bean的概念,这是一组为 Java 对象提供属性支持的约定。在本节中,我们讨论指定 JavaFXBean属性的命名约定,几种方法实现 JavaFX Bean属性,最后使用选择绑定。

将JavaFX Bean适配成JavaFX属性

package sample;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import java.beans.PropertyVetoException;

public class JavaBeanPropertiesExample {
    public static void main(String[] args) throws NoSuchMethodException {
        adaptJavaBeansProperty();
        adaptBoundProperty();
        adaptConstrainedProperty();
    }
    private static void adaptJavaBeansProperty() throws NoSuchMethodException {
        Person person = new Person();
        JavaBeanStringProperty nameProperty = JavaBeanStringPropertyBuilder.create()
                .bean(person)
                .name("name")
                .build();
        nameProperty.addListener((observable, oldValue, newValue) -> {
            System.out.println("JavaFX property " + observable + " changed:");
            System.out.println("\toldValue = " + oldValue + ", newValue = " + newValue);
        });
        System.out.println("Setting name on the JavaBeans property");
        person.setName("Weiqi Gao");
        System.out.println("Calling fireValueChange");

        nameProperty.fireValueChangedEvent();
        System.out.println("nameProperty.get() = " + nameProperty.get());
        System.out.println("Setting value on the JavaFX property");
        nameProperty.set("Johan Vos");
        System.out.println("person.getName() = " + person.getName());
    }
    private static void adaptBoundProperty() throws NoSuchMethodException {
        System.out.println();
        Person person = new Person();
        JavaBeanStringProperty addressProperty = JavaBeanStringPropertyBuilder.create()
                .bean(person)
                .name("address")
                .build();
        addressProperty.addListener((observable, oldValue, newValue) -> {
            System.out.println("JavaFX property " + observable + " changed:");
            System.out.println("\toldValue = " + oldValue + ", newValue = " + newValue);
        });
        System.out.println("Setting address on the JavaBeans property");
        person.setAddress("12345 main Street");
    }
    private static void adaptConstrainedProperty() throws NoSuchMethodException {
        System.out.println();
        Person person = new Person();
        JavaBeanStringProperty phoneNumberProperty = JavaBeanStringPropertyBuilder.create()
                .bean(person)
                .name("phoneNumber")
                .build();
        phoneNumberProperty.addListener((observable, oldValue, newValue) -> {
            System.out.println("JavaFX property " + observable + " changed:");
            System.out.println("\toldValue = " + oldValue + ", newValue = " + newValue);
        });
        System.out.println("Setting phoneNumber on the JavaBeans property");
        try {
            person.setPhoneNumber("800-555-1212");
        } catch (PropertyVetoException e) {
            System.out.println("A JavaBeans property change is vetoed.");
        }
        System.out.println("Bind phoneNumberProperty to another property");
        SimpleStringProperty stringProperty = new SimpleStringProperty("866-555-1212");
        phoneNumberProperty.bind(stringProperty);

        System.out.println("Setting phoneNumber on the JavaBeans property");
        try {
            person.setPhoneNumber("888-555-1212");
        } catch (PropertyVetoException e) {
            System.out.println("A JavaBeans property change is vetoed.");
        }
        System.out.println("person.getPhoneNumber() = " + person.getPhoneNumber());
    }
}


package sample;

/**
 * @author: Administrator
 * @date: 2021/03/20 20:16
 * @description:
 */
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
public class Person {
    private PropertyChangeSupport propertyChangeSupport;
    private VetoableChangeSupport vetoableChangeSupport;
    private String name;
    private String address;
    private String phoneNumber;
    public Person() {
        propertyChangeSupport = new PropertyChangeSupport(this);
        vetoableChangeSupport = new VetoableChangeSupport(this);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        String oldAddress = this.address;
        this.address = address;
        propertyChangeSupport.firePropertyChange("address", oldAddress, this.address);
    }
    public String getPhoneNumber() {
        return phoneNumber;
    }
    public void setPhoneNumber(String phoneNumber) throws PropertyVetoException {
        String oldPhoneNumber = this.phoneNumber;
        vetoableChangeSupport.fireVetoableChange("phoneNumber", oldPhoneNumber, phoneNumber);
        this.phoneNumber = phoneNumber;
        propertyChangeSupport.firePropertyChange("phoneNumber", oldPhoneNumber, this.phoneNumber);
    }
    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }
    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }
    public PropertyChangeListener[] getPropertyChangeListeners() {
        return propertyChangeSupport.getPropertyChangeListeners();
    }
    public void addVetoableChangeListener(VetoableChangeListener l) {
        vetoableChangeSupport.addVetoableChangeListener(l);
    }
    public void removeVetoableChangeListener(VetoableChangeListener l) {
        vetoableChangeSupport.removeVetoableChangeListener(l);
    }
    public VetoableChangeListener[] getVetoableChangeListeners() {
        return vetoableChangeSupport.getVetoableChangeListeners();
    }
}

通过直接实例化属性策略是实现 JavaFX Bean属性的最简单方法。对于每一个
JavaFXBean属性,你想在一个对象中定义,你介绍了一个私人领域的类,是适当的 JavaFX 属性和绑定框架属性类型。这些私有属性只在Bean类被构造时被实例化。获取器和设置器只需调用私有字段的get()和set()方法。

package sample;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.paint.Color;
public class JavaFXBeanModelExample {
    private IntegerProperty i = new SimpleIntegerProperty(this, "i", 0);
    private StringProperty str = new SimpleStringProperty(this, "str", "Hello");
    private ObjectProperty<Color> color = new SimpleObjectProperty<Color>(this, "color", Color.BLACK);
    public final int getI() {
        return i.get();
    }
    public final void setI(int i) {
        this.i.set(i);
    }
    public IntegerProperty iProperty() {
        return i;
    }
    public final String getStr() {
        return str.get();
    }
    public final void setStr(String str) {
        this.str.set(str);
    }
    public StringProperty strProperty() {
        return str;
    }
    public final Color getColor() {
        return color.get();
    }
    public final void setColor(Color color) {
        this.color.set(color);
    }
    public ObjectProperty<Color> colorProperty() {
        return color;
    }
}```