【翻译】Using JavaFX Properties and Binding

原文地址:Using JavaFX Properties and Bindingjava

经过这份指南你能够学到如何在JavaFX中使用属性及其绑定。编程

本指南描述了相关的api,并提供了可编译运行的示例代码。设计模式


概述

许多年来,Java语言都使用JavaBeans组件体系结构来表示对象的属性(Properties)。此模型由一套API和一种设计模式组成;这已是为广大的Java应用程序开发人员和开发工具所广泛了解的了。在本次版本上,表示对象属性(Properties)的支持被添加进了JavaFX,这个支持创建在久经考验的JavaBeans模型基础上,并在其上作了扩展和提高。api

JavaFX的属性(Properties)一般与绑定(Binding)(一种用来表示变量之间直接关系的强大机制)一块儿使用。若对象参与了绑定(Binding),此时对一个对象所作的更改将自动反映到另外一个对象上。这种机制对于各类各样的应用程序来讲可能都是有用的。例如,可用于帐单跟踪程序,当单张帐单发生了变更,总帐单就会自动被更新。或者也可用于一个图形用户界面(GUI),以此实现应用程序的数据的显示可以同步更新。
oracle

绑定(Binding)是由一个或多个依赖项组装而成的。绑定(Binding)监测依赖项列表的变更,并在发现依赖项变更时自动更新。
less

绑定(Binding)的API分为两大类: ide

  1. 高级API:提供了一种简单的途径来建立最多见的绑定(Binding)。它的语法很容易学习和使用,特别是在那些提供代码补全的编程环境里,好比NetBeans IDE。工具

  2. 低级API:提供了额外的灵活性,能够由高级开发人员在高级API不够用的状况下使用。低级API是为快速执行和小内存占用而设计的。学习

本教程的其他部分描述这些api,并提供了代码示例,你能够编译和运行它。开发工具


理解属性(Properties)

正如在概述里提到的,JavaFX的属性(Properties)支持是以创建在JavaBeans组件体系结构上的属性模型(property model)为基础的。本节先简要概述这意味着什么,而后再解释如何在JavaFX中应用属性(Properties)。

Java语言能够把数据封装到一个对象里,但并无对你定义的方法作任何强制的命名约定。例如,你的代码可能会定义一个Person类,封装了first name和last name。没有命名约定的话,不一样的开发人员彻底可能为这些方法选择不一样的命名:read_first(),firstName()或getFN()等都是彻底合法的。然而,这样将没法保证这些名字对于其余开发人员来讲也是能见名知义的。

JavaBeans组件体系结构经过定义一些简单的对全部程序都相容的命名约定来解决这个问题。在JavaBeans编程中,这些方法的完整签名是:  public void setFirstName(String name), public String getFirstName(), public void setLastName(String name), and public String getLastName()。这种命名模式对于开发人员或者开发工具(例如NetBeans IDE)来讲都是很容易辨认的。经过这样的定义,按JavaBeans术语来讲,这时的Person对象包含有firstName和lastName属性。

JavaBeans模型还提供了对复杂属性类型的支持,和附加一个事件分发系统。它还包含许多支持类,全部可用的API都在java.bean包底下。所以,掌握JavaBeans编程涉及到学习所需的命名约定和其相应的API。(更多关于JavaBeans的背景阅读请看 the JavaBeans lesson of the Java Tutorial)。

一样的,理解JavaFX的属性(Properties)还须要学习一些新的API和命名约定。在JavaFX中,你彻底有可能只感兴趣于使用那些包含属性(Properties)的类(而不是在你的定制类中本身实现),例1 - 1会带你熟悉JavaFX属性模式形式下的新的方法命名约定。例1 - 1定义了一个Bill类,并含有一个单独的属性amountDue。

例1 - 1 定义一个属性

package propertydemo;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
 
class Bill {
 
    // 定义一个保存属性的变量
    private DoubleProperty amountDue = new SimpleDoubleProperty();
 
    // 定义一个读取器来获取属性的值
    public final double getAmountDue(){return amountDue.get();}
 
    // 定义一个设置器来设置属性的值
    public final void setAmountDue(double value){amountDue.set(value);}
 
     // 定义一个获取属性自身的读取器
    public DoubleProperty amountDueProperty() {return amountDue;}
 
}

amountDue对象——一个javafx.beans.property.DoubleProperty的实例——被标记为private以使其对外不可见。这些在Java和JavaBeans应用程序开发中都是一种标准实践。然而须要注意的是,此对象的类型不是一个标准的Java原语,而是个封装了一个Java原语并添加了一些额外的功能的新的包装类(全部位于javafx.beans.property包底下的类都内建对属性与绑定的支持)。

属性方法命名约定以下:

  • getAmountDue()方法是一个标准的值读取器,它返回amountDue属性的当前值。按照约定, 此方法被声明为final。注意它的返回类型是double, 而不是DoubleProperty

  • setAmountDue(double)方法(也被声明为final)是一个标准的设置器,它容许调用者去设置属性值。设置器是无关紧要的,它接收的参数类型也是double

  • 最后,amountDueProperty()方法是一个属性读取器。这是一种新的约定,先是写上属性名(在本例中就是amountDue), 后面再接上“Property”。方法的返回类型则与属性自身的类型相同(在本例中就是DoubleProperty).

当用JavaFX构建GUI应用程序时,你会发现某些类的API已经有了对属性的实施。例如,javafx.scene.shape.Rectangle类包含了arcHeight, arcWidth, height, width, x, and y等属性。这些属性将会有与先前描述的约定匹配的相应的方法。例如,getArcHeight(),setArcHeight(double),arcHeightProperty(),它们一块儿(对开发人员与工具)指明了给定属性(Properties)的存在。 

你还能够经过添加一个change listener来收到属性值的变更通知。如例1 - 2所示。

例1 - 2 使用ChangeListener

package propertydemo;
 
import javafx.beans.value.ObservableValue;
import javafx.beans.value.ChangeListener;
 
public class Main {
 
    public static void main(String[] args) {
 
      Bill electricBill = new Bill();
 
       electricBill.amountDueProperty().addListener(new ChangeListener(){
        @Override public void changed(ObservableValue o,Object oldVal, 
                 Object newVal){
             System.out.println("Electric bill has changed!");
        }
      });
     
      electricBill.setAmountDue(100.00);
     
    }
}

运行这份代码将会在控制台打印"Electric bill has changed",这说明change listener起了做用。


使用高级绑定API

高级API是在你的应用程序中开始使用绑定的最快和最简单的方法。它由两部分组成:流式API(Fluent API),和Bindings类。流式API公布在各类依赖对象上的方法,而绑定类则提供了静态工厂方法。

为了开始使用流式API,咱们来考虑一个简单的案例:绑定两个整数以使它们的值在任什么时候候都会加到一块儿。在例1-3中,涉及到三个变量:num1(依赖项),num2(依赖项),和sum(绑定项)。依赖项类型均为IntegerProperty,绑定项则是NumberBinding。

例1 - 3 使用流式API

package bindingdemo;
 
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.binding.NumberBinding;
 
public class Main {
 
    public static void main(String[] args) {
        IntegerProperty num1 = new SimpleIntegerProperty(1);
        IntegerProperty num2 = new SimpleIntegerProperty(2);
        NumberBinding sum = num1.add(num2);
        System.out.println(sum.getValue());
        num1.set(2);
        System.out.println(sum.getValue());
    }
}

这份代码绑定了两个依赖项,并打印它们的和(sum变量),接着改变num1变量的值再打印一遍和。输出结果为“3”和“4”。这说明绑定起了做用。

你还可使用Bindings类来完成相同的事情,如例1-4所示。

例1 - 4 使用Bindings类

package bindingdemo;
 
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.Bindings;
 
public class Main {
 
    public static void main(String[] args) {
       IntegerProperty num1 = new SimpleIntegerProperty(1);
       IntegerProperty num2 = new SimpleIntegerProperty(2);
       NumberBinding sum = Bindings.add(num1,num2);
       System.out.println(sum.getValue());
       num1.setValue(2);
       System.err.println(sum.getValue());
    }
}

例1-5结合了两种方式:

例1 - 5 结合两种方式

package bindingdemo;
 
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.Bindings;
 
public class Main {
 
    public static void main(String[] args) {
       IntegerProperty num1 = new SimpleIntegerProperty(1);
       IntegerProperty num2 = new SimpleIntegerProperty(2);
       IntegerProperty num3 = new SimpleIntegerProperty(3);
       IntegerProperty num4 = new SimpleIntegerProperty(4);
       NumberBinding total =
         Bindings.add(num1.multiply(num2),num3.multiply(num4));
       System.out.println(total.getValue());
       num1.setValue(2);
       System.err.println(total.getValue());
    }
}

例1-5把代码改成调用来自流式API的multiply方法,并从Bindings类调用add静态方法。你能够从中了解到高级API容许你在作算术运算时混合类型,而且运算结果的类型的约定跟Java语言的约定相同。

  1. 若是其中一个操做数是double类型,则结果也为double类型。

  2. 不然若其中一个是float类型,则结果为float类型。

  3. 不然若其中一个是long类型,则结果为long类型。

  4. 不然结果为int类型。

下一节将探索可观察性(observability),并经过示例展现invalidation listeners和change listeners的差异。


探索 Observable,ObservableValue,InvalidationListener和ChangeListener

绑定API定义了一系列的接口,这些接口使得对象能在值发生变更后收到变更或者失效的通知。Observable和 ObservableValue接口发出变更通知,InvalidationListener和ChangeListener接口则负责接收这些通知。不一样在于ObservableValue包裹了一个值并会发送其值的变更消息给那些已注册的ChangeListener,而Observable没有包裹任何值,仅仅把变更消息发送给已注册的InvalidationListener。

JavaFX的绑定和属性都支持惰性求值,这意味着当发生了变更,并不会当即去从新计算值,仅当这个值随后被请求了才会去从新计算。

在例1-6中,帐单的总额(一个绑定项)会在第一次监测到其中一个依赖项发生变更后被置为失效,然而这个绑定项会在被再次请求时从新计算。

例1 - 6 使用InvalidationListener

package bindingdemo;
 
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.Bindings;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
 
class Bill {
 
    // 定义一个属性
    private DoubleProperty amountDue = new SimpleDoubleProperty();
 
    // 定义一个读取器来获取属性的值
    public final double getAmountDue(){return amountDue.get();}
 
    // 定义一个设置器来设置属性的值
    public final void setAmountDue(double value){amountDue.set(value);}
 
     // 定义一个获取属性自身的读取器
    public DoubleProperty amountDueProperty() {return amountDue;}
 
}
 
public class Main {
 
    public static void main(String[] args) {
 
        Bill bill1 = new Bill();
        Bill bill2 = new Bill();
        Bill bill3 = new Bill();
 
        NumberBinding total =
          Bindings.add(bill1.amountDueProperty().add(bill2.amountDueProperty()),
              bill3.amountDueProperty());
        total.addListener(new InvalidationListener() {
 
        @Override public void invalidated(Observable o) {
                System.out.println("The binding is now invalid.");
            }
        });

        // 第一次调用使得绑定项变为失效
        bill1.setAmountDue(200.00);

        // 此时绑定项为失效
        bill2.setAmountDue(100.00);
        bill3.setAmountDue(75.00);

        // 请求total值(调用getValue())使绑定项变为有效
        System.out.println(total.getValue());

        // 变为失效
        bill3.setAmountDue(150.00);

        // 变为有效
        System.out.println(total.getValue());
    }
}

在改变了单张帐单的值后,绑定项就变成了失效的了,同时invalidation listener会被触发。但若是绑定项在以前已是失效状态的话,则不会被触发。即便其余依赖项再次发生了变更(在例1-6中,调用total.getValue()会使绑定项从失效变为有效)。咱们能够经过在调用total.getValue()以后,invalidation listener会在随后第一次变更任一依赖项后被触发一次,而当其仍为失效状态时永远没被触发的这个事实来了解到这一点。

需注意到注册一个ChangeListener将会强制执行当即求值,尽管ObservableValue是被实现为支持惰性求值的。对于惰性求值来讲,直到被从新计算以前都不可能确切地知道一个已失效的值是否发生了变更。为此,失效事件在懒惰或当即的实现上均可以被发出,而发生了变更事件后则须要当即求值[*1]


使用低级绑定API

若是高级API不能知足你程序的需求,你可使用低级API。低级API对于开发人员来讲能比高级API提供更多的灵活性。例1-7演示了一个使用低级API的简单例子。

例1 - 6 使用低级API

package bindingdemo;
 
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.binding.DoubleBinding;
 
public class Main {
 
    public static void main(String[] args) {
 
        final DoubleProperty a = new SimpleDoubleProperty(1);
        final DoubleProperty b = new SimpleDoubleProperty(2);
        final DoubleProperty c = new SimpleDoubleProperty(3);
        final DoubleProperty d = new SimpleDoubleProperty(4);
 
        DoubleBinding db = new DoubleBinding() {
 
            {
                super.bind(a, b, c, d);
            }
 
            @Override
            protected double computeValue() {
                return (a.get() * b.get()) + (c.get() * d.get());
            }
        };
 
        System.out.println(db.get());
        b.set(3);
        System.out.println(db.get());
    }
}

使用低级API须要继承一个绑定类而且重写其负责返回绑定项当前值的computeValue()方法。例1-7自定义了一个DoubleBinding的子类。调用super.bind()能够把全部依赖项传递给其父类以此得以保留DoubleBinding的缺省的‘失效’行为。通常来讲对绑定项是否失效的检查不须要你去作了,由于这些行为已经由基类为你提供了。

到此为止,你对开始使用低级API已了解到了足够的信息。


[*1]译者注:读者可在此代码基础上在total上再添加一个ChangeListener,在重写其changed方法时打印第三个参数newValue,这时再运行代码则会看到打印出来的newValue的值都是最新的total值,并且每次依赖项值变更都会触发一次InvalidationListener,这时的invalidation listener不须要total值被再次请求也能够被当即触发了。

相关文章
相关标签/搜索