第 8 课:数据绑定和触发器

 第 8 课:数据绑定和触发器

数据绑定(即在两个变量之间创建直接关系的功能)是 JavaFX Script 编程语言最为强大的功能之一。本课首先介绍如何绑定两个简单的变量,然后介绍如何在一个变量和一个函数/表达式的结果之间建立更复杂的绑定。在了解了这一概念之后,可以参阅对 UI 对象应用数据绑定使用 JavaFX 构建 GUI 应用程序中的一课),该课程举例说明了在构建 JavaFX 应用程序时数据绑定是一个多么强大的工具。

替换触发器是附加到变量的代码块-当变量发生变化时,代码会自动执行。本课提供了替换触发器的实际应用。
 
目录

 

绑定概述
绑定和对象
绑定和函数
对序列进行绑定
替换触发器
 
绑定概述

bind 关键字将目标变量的值与绑定表达式的值相关联。绑定表达式可以是某个基本类型的简单值、对象、函数的结果或表达式的结果。以下几节分别提供每种绑定表达式的示例。

绑定和对象

在大多数实际的编程情况下,需要通过数据绑定使应用程序的图形用户界面 (Graphical User Interface, GUI) 与其底层数据同步。(GUI 编程是使用 JavaFX 构建 GUI 应用程序的主题;下面我们将用一些非可视的示例来说明基本的底层结构。)

让我们先看一个简单的示例:下面的脚本将变量 x 绑定到变量 y,更改 x 的值,然后输出 y 的值。由于这两个变量绑定在一起,因此 y 值会自动更新为新值。

var x = 0;
def y = bind x;
x = 1;
println(y); // y now equals 1
x = 47;
println(y); // y now equals 47
 

请注意,我们已将变量 y 声明为 def。这可防止任何代码直接为该变量赋值(尽管允许该变量的值因绑定 (bind) 而更改)。在绑定到对象时,此约定仍适用(请回顾使用对象中介绍的 Address):

var myStreet = "1 Main Street";
var myCity = "Santa Clara";
var myState = "CA";
var myZip = "95050";

def address = bind Address {
street: myStreet;
city: myCity;
state: myState;
zip: myZip;
};

println("address.street == {address.street}");
myStreet = "100 Maple Street";
println("address.street == {address.street}");
 

如果更改 myStreet 的值,address 对象内部的 street 变量将受到影响:

address.street == 1 Main Street
address.street == 100 Maple Street
 

请注意,更改 myStreet 的值实际上会导致创建一个新的 Address 对象,然后将该对象重新赋给 address 变量。为了跟踪所做的更改而创建新的 Address 对象,请改为直接绑定 (bind) 到该对象的实例变量:

def address = bind Address {
street: bind myStreet;
city: bind myCity;
state: bind myState;
zip: bind myZip;
};
 

如果要显式绑定到实例变量,还可以省略第一个 bindAddress 前面的那个):

def address = Address {
street: bind myStreet;
city: bind myCity;
state: bind myState;
zip: bind myZip;
};
 
绑定和函数

前面的课程已讲授了函数,但是您还必须了解绑定函数非绑定函数之间的区别。

请考虑下面的函数,该函数创建和返回一个 Point 对象:

var scale = 1.0;

bound function makePoint(xPos : Number, yPos : Number) : Point {
Point {
x: xPos * scale
y: yPos * scale
}
}

class Point {
var x : Number;
var y : Number;
}
 

这就是所谓的绑定函数,因为它前面有 bound 关键字。


注意: bound 关键字不能替换 bind 关键字;这两个关键字按如下所示方式结合使用。

接下来,让我们添加一些代码来调用此函数并测试绑定:

var scale = 1.0;

bound function makePoint(xPos : Number, yPos : Number) : Point {
Point {
x: xPos * scale
y: yPos * scale
}
}

class Point {
var x : Number;
var y : Number;
}

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);

myX = 10.0;
println(pt.x);

scale = 2.0;
println(pt.x);
 

此脚本的输出如下所示:

3.0
10.0
20.0
 

让我们分析一下此脚本(一次分析一部分)。

代码:

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);
 

将脚本变量 myXmyY 初始化为 3.0。这些值随后作为参数传递给 makePoint 函数,该函数会创建并返回一个新的 Point 对象。bind 关键字(位于 makePoint 调用前面)将新创建的 Point 对象 (pt) 绑定到 makePoint 函数的结果。

接下来,代码:

myX = 10.0;
println(pt.x);
 

myX 的值更改为 10.0 并输出 pt.x 的值。输出表明 pt.x 现在也为 10.0

最后,代码:

scale = 2.0;
println(pt.x);
 

更改 scale 的值并再次输出 pt.x 的值。pt.x 的值现在为 20.0。但是,如果我们从该函数中删除 bound 关键字(从而使其成为非绑定函数),则输出应为:

3.0
10.0
10.0
 

这是因为,非绑定函数只是在其某个参数发生变化时才被重新调用。由于 scale 不是函数的参数,因此更改它的值将不会导致另一个函数调用。

对序列进行绑定

您还可以将 bindfor 表达式结合使用。为了对此进行研究,让我们首先定义两个序列并输出这两个序列中各个项的值:

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
printSeqs();

function printSeqs() {
println("First Sequence:");
for (i in seq1){println(i);}
println("Second Sequence:");
for (i in seq2){println(i);}
}
 

seq1 包含十个项(数字 1 至 10)。seq2 也包含十个项;这些项本来会与 seq1 具有相同的值,但是我们已经对其中的每个项都应用了表达式 item*2,因此它们的值将加倍。

因此,输出为:

First Sequence:
1
2
3
4
5
6
7
8
9
10
Second Sequence:
2
4
6
8
10
12
14
16
18
20
 

我们可以通过将 bind 关键字放在 for 关键字前面来绑定这两个序列。

def seq2 = bind for (item in seq1) item*2;
 

问题现在变成:“如果 seq1 发生了某些变化,那么是 seq2 中的所有项都受到影响还是部分项受到影响?”我们可以通过以下方法来对此进行测试:将一个项(值 11)插入 seq1 的末尾处,然后输出这两个序列的值,看有什么变化:

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
insert 11 into seq1;
printSeqs();

function printSeqs() {
println("First Sequence:");
for (i in seq1){println(i);}
println("Second Sequence:");
for (i in seq2){println(i);}
}
 

输出:

First Sequence:
1
2
3
4
5
6
7
8
9
10
11
Second Sequence:
2
4
6
8
10
12
14
16
18
20
22
 

输出表明,将 11 插入 seq1 的末尾处不会影响 seq2 中的前 10 个项;新项会自动添加到 seq2 的末尾处,其值为 22。

替换触发器

替换触发器是附加到变量的任意代码块,一旦变量的值发生变化,它们就会执行。以下示例显示了基本语法:它定义一个 password 变量并向其附加一个替换触发器;当密码发生变化时,该触发器会输出一则消息来报告此变量的新值:

var password = "foo" on replace oldValue {
println("/nALERT! Password has changed!");
println("Old Value: {oldValue}");
println("New Value: {password}");
};

password = "bar";
 

此示例的输出如下所示:

ALERT! Password has changed!
Old Value:
New Value: foo

ALERT! Password has changed!
Old Value: foo
New Value: bar
 

此示例中的触发器引发两次:当 password 初始化为 "foo" 时引发一次,当其值变成 "bar" 时又引发一次。请注意,oldValue 变量存储在调用触发器之前变量的值。您可以将 oldValue 变量命名为任何所需的名称,我们是由于该名称具有描述性才恰好使用它。

你可能感兴趣的:(第 8 课:数据绑定和触发器)