DOC-11-01 使用JavaFX属性与绑定

在本篇教程中你将学到如何在JavaFX应用中使用属性与绑定。本篇描述了相关的API并提供了可供编译与运行的范例。

概述

多年来,Java编程语言使用JavaBeans组件结构来表示一个对象的属性。这种模型包含一种API及设计模式;这种模型受到Java应用开发者与许多开发工具的广泛认可。此版本将对属性的支持引入到了JavaFX之中,这种支持基于已被证明过的JavaBeans模型,但同时也做了扩展与改进。

JavaFX属性通常与绑定一起使用,这是一种表示变量之间直接关系的强大机制。当对象参与绑定,对于一个对象的改变将会自动映射到另一个对象上。这种机制在许多应用程序中都非常有用。例如绑定可用于账单发票跟踪程序中,当有一个账单变化时,所有账单的总额能被自动更新。绑定也可用于图形用户界面(GUI)中来保持界面的显示与应用的底层数据自动同步。

绑定由一个或多个源组成,这些源被称之为依赖(dependencies)。一个绑定会观察其所有依赖的变化,并且会在一个变化被检测到后自动更新自己。

绑定的API分为两大类:

1. 高级API:提供一种在大多数常见情况下创建绑定的简单方法。语法易学易用,尤其是在使用NetBeans IDE等提供代码补全的开发环境下。

2. 低级API:在高级API无法满足需求的情况下,为高级开发者提供额外的灵活性。低级API是为了快速执行和低内存占用而设计。

本教程余下部分描述了这些API,并提供了可以编译并运行的代码。

理解属性

如概述中所提到的,JavaFX中对属性的支持是基于著名的JavaBeans组件结构的属性模型。这一节简单描述了其意义,并解释了如何在JavaFX中应用属性。

Java编程语言允许在对象中封装数据,但并未强制要求你的自定义方法一定要遵循特定命名规范。例如,你可能自定义了一个Person类,其中包含了一个姓和一个名。如果没有命名约定,不同的程序员可能为这些方法采取不同的命名方式:read_first(), firstName(), getFN()等,这些都是完全有效的选择。然而我们无法保证这些命名对于其他的开发者都有意义。

JavaBeans组件架构通过定义一些简单的命名规范,引入跨项目的一致性来解决了这个问题。在使用JavaBeans编程的过程中,上述方法的完整签名应为:public void setFirstName(String name), public String getFirstName(), public void setLastName(String name) 与 public String getLastName()。这种命名模式对于程序员和类似NetBeans IDE等编辑工具都很容易识别。使用JavaBeans术语来说,Person对象被称为包含firstName和lastName两个属性(properties)。

JavaBeans模型也支持复杂属性类型,并且提供了一个事件分发系统。其中包括许多支持类,它们都可以在java.beans包中作为API找到。因此掌握JavaBeans编程包括学习必要的命名约定与相应的API。(可以在Java教程中的JavaBeans章节中学习相关背景知识)

同样,要理解JavaFX属性同样需要学习一些新的API和命名规范。在JavaFX中,你完全可能仅对使用包含属性的类感兴趣(而不想在你的自定义类中实现属性),但在例1-1中将使你了解JavaFX属性模式中新的方法命名规范。在样例中定义了一个名为Bill的类,它实现了一个名为amountDue的属性。

例1-1 定义一个属性

amountDue对象是javafx.beans.property.DoubleProperty的一个实例,并使用private对其进行了标记,从而将其封装起来与外部隔离。这是Java和JavaBeans应用开发中的一个标准做法。注意虽然这个对象的类型不是标准Java基本类型,而是一个新的包装类,其中封装了基本类型并增加了一些额外方法(javafx.beans.property包下的类的设计中都包含了对可观察性和绑定的内建支持)。

属性方法的命名规范如下:

● getAmountDue()方法是标准的取值方法(getter),返回了amountDue属性的当前值。按照规范,这个方法应该声明为final。需要注意这个方法的返回类型为double,而不是DoubleProperty。

● setAmountDue(double)方法(同样也是final的)是一个标准的设值方法(setter),它允许调用者设置属性值。setter方法是可选的。其参数类型同样也是double。

● 最后,amountDueProperty()方法定义了属性的getter方法。这是一条新规范,它要求方法名包含属性名(在本例中是amountDue),以单词Property结尾。返回类型与属性本身一致(在本例中是DoubleProperty)。

当使用JavaFX建立GUI应用程序时,你将会注意到API中的某些类都已经实现了属性。例如javafx.scene.shape.Rectangle类包含了arcHeight, arcWidth, height, width, x和y这几个属性。其中每个属性都有像前面描述的规范那样对应的方法。例如getArcHeight(), setArcHeight(double), arcHeightProperty()这些方法都(对开发者和开发工具)表明了对应属性的存在。

你也可以添加一个改变监听器(change listener)来在属性值改变时获得通知,如例1-2所示。

例1-2 使用一个ChangeListener

运行这个例子将会向标准输出打印”Electric bill has changed”这条信息,以此证明change listener通知运行正常。

使用高级绑定API

高级API是在你的应用程序中开始使用绑定的最简单快捷的方法。它包含两部分:Fluent API和Binding类。Fluent API在各种依赖对象上暴露方法,而Binding类提供了静态工厂方法。

要开始使用Fluent API时,可以考虑这样一个简单的例子:有两个整数被绑定在一起,所以它们的值总是被相加到一起。在例1-3中包含三个变量:num1(依赖变量),num2(依赖变量)和sum(绑定变量)。依赖变量的类型都是IntegerProperty,而绑定变量的类型是NumberBinding。

例1-3 使用Fluent API

这段代码绑定了两个依赖变量,输出它们的和,然后改变num1的值并再次输出它们的和。对应的结果是“3”和“4”,这证明了绑定是有效的。

你也可以使用Bindings类来做同样的事,如例1-4所示。

例1-4 使用Bindings

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

例1-5 结合两种方法

例1-5修改代码来调用了Fluent API中的multiply方法与Binding类的add方法。你也应该了解高级API在定义算数操作时允许进行类型的混合。计算结果类型的定义方式也与Java编程语言一样:

1. 如果一个操作数是double,结果是double。

2. 否则,如果一个操作数是float,结果是float。

3. 否则,如果一个操作数是long,结果是long。

4. 否则结果类型为integer。

下一节将对可见性进行探索,并展示了失效监听器(invalidation listener)与change listener的区别。

探索Observable, ObservableValue, InvalidationListener和ChangeListener

绑定API定义了一系列的接口,可以做到当一个值发生改变或者失效时可以通知对象。Observable与ObservableValue接口触发改变通知,而InvalidationListener和ChangeListener接口接收通知。两者的区别是ObservableValue包装了一个值并触发此值的改变到任何已注册的ChangeListener上,而Observable(并未包装一个值)是触发改变到任何已注册的InvalidationListener上。

JavaFX绑定与属性的实现都支持延迟计算(lazy evaluation),意思是当改变发生时值并不是立即重新计算。当此值随后被请求时才进行重新计算。

在例1-6中,账单总数(它是一个绑定)会在第一次检测到其中一个依赖变量改变时被标记为无效。然而,只有当总数被再次请求时,这个绑定对象才会重新计算自己的值。

例1-6 使用InvalidationListener

由于改变了单个账单的值,对应的绑定会变为无效,因此InvalidationListener会被触发。但是如果绑定已经无效了,即使另一个账单发生了改变,InvalidationListener也不会被再次触发。(例1-6中,调用total.getValue()方法将绑定从无效变为有效。)我们知道这是因为对任何一个在依赖列表中的账单的后续改变都会导致InvalidationListener被再次触发。这些在绑定仍然是无效的情况下是不会发生的。

注意如果注册一个ChangeListener

使用低级绑定API

如果高级API无法满足你的需求,那么你可以用低级API来替代。低级API相比高级API为开发者提供了更高的灵活性(或更高性能)。

例1-7展示了使用低级API的一个简单例子。

例1-7 使用低级API

使用低级API包括对某一个绑定类进行扩展并重写其computeValue()方法以返回绑定的当前值。例1-7中使用了DoubleBinding 的一个自定义子类。子类中调用super.bind()方法将依赖变量向上传递给了DoubleBinding类,所以默认的失效行为会被保留。一般不需要检查绑定是否是失效;基类会为你提供这种行为。

现在你已经知道了足够的信息来开始使用低级API。

打赏一下
支付宝
微信
除非注明,博客文章均为原创,转载请标明文章地址
本文地址: http://www.javafxchina.net/blog/2015/08/javafx-properties-binding/
百度已收录