This tutorial describes properties and binding in JavaFX 2.0, using working examples that you can compile and run.(这个指南讨论JavaFX2.0中的属性和绑定,在讨论的过程中会使用工作中的例子,这些例子都是可以编译和运行的。)

Overview

For many years, the Java programming language has used the JavaBeans component architecture to represent the property of an object.(很多年来,在java中使用JavaBeans组件来表示对象的属性。) This model consists of both an API and a design pattern; it is widely understood by Java application developers and development tools alike. (这个模型是由API和设计模型组成的;java开发者和开发工具已经广为知道了的。)This release introduces property support into JavaFX, support that is based on the proven JavaBeans model, but expanded and improved.(这个版本引入了属性支持到JavaFX中,支持基本的JavaBeans模型,但是做了扩展和提高。)

JavaFX properties are often used in conjunction with binding, a powerful mechanism for expressing direct relationships between variables.(Java的属性经常和绑定连结在一起,一个强大的表示变量间直接关系的机制。) When objects participate in bindings, changes made to one object will automatically be reflected in another object.(当对象进行了绑定,一个对象的改变将会自动反映到另一个对象上。) This can be useful in a variety of applications.(在非常负载的程序中这是非常有用的。) For example, binding could be used in a bill invoice tracking program, where the total of all bills would automatically be updated whenever an individual bill is changed. (例如,绑定可以用在账单发票绑定程序中,当一个账单改变时所有的账单总数会自动更新。)Or, binding could be used in a graphical user interface (GUI) that automatically keeps its display synchronized with the application's underlying data.(或者,绑定可以用在图形界面中,当后台的程序发生改变的时候前台会实时同步跟新显示。)

Bindings are assembled from one or more sources, known as dependencies.(绑定是由一个或多个来源组合成,例如依赖。) A binding observes its list of dependencies for changes, and then updates itself automatically after a change has been detected.(绑定将会观察依赖列表的改变,当检测到改变将会自动进行更新。)

The binding APIs are divided into two broad categories:(绑定的API来自两个很广的种类:)

  1. The High-Level API: Provides a simple way to create bindings for the most common use cases. Its syntax is easy to learn and use, especially in environments that provide code completion, such as the NetBeans IDE.(高层次的API:提供了简单的方法来尾大多数的用户事件创建绑定。它的语法是很容易学习和使用的,特别是提供了代码自动完成,例如NetBeans IDE。)

  2. The Low-Level API: Provides additional flexibility, and can be used by advanced developers in situations where the High-Level API is insufficient. The Low-Level API was designed for fast execution and small memory footprint.(低水平的API:提供了更多的灵活性,能够高级API是不够使用的时候给高级开发者使用。的API是设计来使用在更快速执行和更小内存占用。)

The remainder of this tutorial describes these APIs, and provides working code examples that you can compile and run.(下面部分教程是将API和提供可以运行的代码例子。)

 

Understanding Properties(明白属性)

As mentioned in the overview, JavaFX property support is based on the well-known property model established by the JavaBeans component architecture.(在中提到JavaFX属性支持是基于众所周知的建立在JavaBean组件基础上属性模型)This section provides a brief overview of what that means, then explains how properties apply to JavaFX.(这部分解释这是什么,接下来解释在JavaFX中怎么使用属性。)

The Java programming language makes it possible to encapsulate data within an object, but it does not enforce any specific naming conventions for the methods that you define.(java语言让数据封装在对象中称为可能,但是它并不强迫任何指定名字的规范来指导你怎么定义方法。) For example, your code might define a Person class, which encapsulates a first name and a last name. (例如,你的代码可能定义了一个Person类,它封装了姓和名。)But without naming conventions, different programmers might choose different names for these methods: read_first()firstName()getFN(), etc. would all be perfectly valid choices.(但是没有名字规范,不同的程序员可能给方法命名不同的名字:read_first()firstName()getFN()等等都是很好的选择。) However, there is no guarantee that these names will be meaningful to other developers.(无论如何,不能保证这些名字对其他的开发者来说是由意义的。)

The JavaBeans component architecture addressed this problem by defining some simple naming conventions that bring consistency across projects.(JavaBean组件通过定义简单的命名规范解决这个问题,这个规范让整个工程都很统一。) In JavaBeans programming, the full signatures for these methods would be: public void setFirstName(String name)public String getFirstName(),public void setLastName(String name), and public String getLastName(). (在JavaBean程序中,方法的完整签名是这样的:public void setFirstName(String name)public String getFirstName(),public void setLastName(String name), and public String getLastName())This naming pattern is easily recognizable, both to human programmers and to editing tools, such as the NetBeans IDE.(这个命名模型对程序员和编辑工具来说都是很容易辨别的。) In JavaBeans terminology, the Person object is said to contain firstName and lastName properties.(在JavaBean技术中,Person对象包含了firstName和lastName属性。)

The JavaBeans model also provides support for complex property types, plus an event delivery system.(JavaBeans模型也提供了对复杂类型的支持,添加了事件传递系统。) It also contains a number of support classes, all available as an API under the java.beans package. (它包含了很多支持的类,这些类都可以在java.beans包中找到。)Therefore, mastering JavaBeans programming involves learning the required naming conventions and its corresponding API.(因此,学习掌握javaBean程序包括了学习要求的命名规范和它相关的API。) (For more background reading on JavaBeans in general, see the JavaBeans lesson of the Java Tutorial athttp://download.oracle.com/javase/tutorial/javabeans).(想要了解更多JavaBean的只是,请看JavaBean课程。)

Similarly, understanding JavaFX properties also requires learning a few new APIs and naming conventions.(类似的,明白JavaFX属性也需要懂得一些新的API和名字的规范。) In JavaFX, it is entirely possible that you will only be interested in using classes that contain properties (as opposed to implementing properties in your own custom classes), but Example 1 will familiarize you with the new method naming conventions that form the JavaFX property pattern.(在JavaFX中,你支队使用那些包含属性的类感兴趣是由可能的,与之相反的是在你自己的类中实现属性,在例子1中展示了JavaFX属性模型命名规范的新方法。) It defines a class named Bill, which implements a single property named amountDue.(它定义名为Bill的类,它实现了命名为amountDue的单个属性)

Example 1 Defining a Property

    
    
    
    
  1. package propertydemo; 
  2.  
  3. import javafx.beans.property.DoubleProperty; 
  4. import javafx.beans.property.SimpleDoubleProperty; 
  5.   
  6. class Bill { 
  7.   
  8.     // Define a variable to store the property 
  9.     private DoubleProperty amountDue = new SimpleDoubleProperty(); 
  10.   
  11.     // Define a getter for the property's value 
  12.     public final double getAmountDue(){return amountDue.get();} 
  13.   
  14.     // Define a setter for the property's value 
  15.     public final void setAmountDue(double value){amountDue.set(value);} 
  16.   
  17.      // Define a getter for the property itself 
  18.     public DoubleProperty amountDueProperty() {return amountDue;} 
  19.   

 

The amountDue object — an instance of the javafx.beans.property.DoubleProperty class — is marked as private to encapsulate it from the outside world. (amountDue对象——javafx.beans.property.DoubleProperty类的实例——封装成了私有属性,不让外部世界访问。)This is standard practice in both Java and JavaBeans application development.(在java和JavaBeans应用程序开发中这都是很典型的。) Note however that the object's type is not one of the standard Java primitives, but rather, a new wrapper class that encapsulates a Java primitive and adds some extra functionality (the classes under javafx.beans.property all contain built-in support for observability and binding as part of their design).(注意:这个对象的类型不是java继承类型,是封装了Java原生类型和添加了更多方法的封装类(在javafx.beans.property 下的类都内置了observability和绑定。))

The property method naming conventions are as follows:(这属性方法命名规范如下:)

  • The getAmountDue() method is a standard getter that returns the current value of the amountDueproperty. By convention, this method is declared as final. Note that the return type for this method is double, not DoubleProperty.(getAmountDue方法是标准的get方法,它返回amountDueproperty现在的值。按照惯例,这个方法被定义成final方法。注意返回的类型是double不是DoubleProperty。)

  • The setAmountDue(double) method (also final) is a standard setter that allows a caller to set the property's value. The setter method is optional. Its parameter is also of type double.(setAmountDue是标准的set方法,它允许调用者色绘制属性的值。这个set方法是可选的。它的参数也是类型double。)

  • Finally, the amountDueProperty() method defines the property getter. This is a new convention in which the method name contains the name of the property (amountDue, in this case), followed by the word "Property." The return type is the same as the property itself (DoubleProperty, in this example).(最后,amountDueProperty方法定义了属性的get方法。这是一个新的规范,方法的名字包含了属性的名字,在这个例子中是amountDue,后面跟着Property。这个返回类型和属性它自己的类型是一样的,在这个例子中是DoubleProperty。)

When building GUI applications with JavaFX, you will notice that certain classes in the API already implement properties.(当在JavaFX中建立界面程序的时候,你会注意到在API中某些类已经实现了属性) For example, the javafx.scene.shape.Rectangle class contains properties for arcHeight,arcWidthheightwidthx, and y.(例如,javafx.scene.shape.Rectangle包好了arcHeight,arcWidthheightwidthx, 和y属性。) For each of these properties there will be corresponding methods that match the conventions previously described.(对于这些属性,它们都符合刚才描述的方法规范。) For example, getArcHeight()setArcHeight(double),arcHeightProperty(), which together indicate (to both developers and tools) that the given property exists.(例如, getArcHeight()setArcHeight(double),arcHeightProperty()一起指定给定属性的操作,对于开发者和开发工具都是这样。)

You can also add a change listener to be notified when the property's value has changed, as shown in Example 2.(你也可以添加改变监听器,当属性的值发生变化的时候会自动提醒。如例子2.)

Example 2 Using a ChangeListener

 

     
     
     
     
  1. package propertydemo; 
  2.   
  3. import javafx.beans.value.ObservableValue; 
  4. import javafx.beans.value.ChangeListener; 
  5.   
  6. public class Main { 
  7.   
  8.     public static void main(String[] args) { 
  9.   
  10.       Bill electricBill = new Bill(); 
  11.   
  12.        electricBill.amountDueProperty().addListener(new ChangeListener(){ 
  13.         @Override public void changed(ObservableValue o,Object oldVal,  
  14.                  Object newVal){ 
  15.              System.out.println("Electric bill has changed!"); 
  16.         } 
  17.       }); 
  18.       
  19.       electricBill.setAmountDue(100.00); 
  20.       
  21.     } 

Running this example will print the message "Electric bill has changed" to standard output, proving that the change listener notification is working.(运行这个例子将会打印信息"Electric bill has changed"到标准输出,证明了变化监听其是在工作的。)

Using the High-Level Binding API(使用高级绑定API)

The High-Level API is the quickest and easiest way to begin using bindings in your own applications.(高级API使用在你的程序中是很快捷和容易的。) It consists of two parts: the Fluent API, and the Bindings class. (它包好了两部分:Fluent API和绑定类。)The Fluent API exposes methods on the various dependency objects, whereas the Bindings class provides static factory methods instead.(Fluent API公开对依赖的对象公开方法,绑定类提供了工厂静态方法。)

To begin using the Fluent API, consider a simple use case in which two integers are bound so that their values are always added together. (在开始使用Fluent API之前,考虑一个简单的实例,两个整数被绑定,以便它们的值总是可以加在一起。)In Example 3, there are three variables involved: num1 (a dependency), num2 (a dependency), and sum (the binding). (在例子3中,有三个变量包含在内:numl(依赖),num2 (依赖),和sum(绑定))The dependency types are both IntegerProperty, and the binding itself is NumberBinding.(依赖的类型都是IntegerProperty,绑定的类型是NumberBinding。)

Example 3 Using the Fluent API

     
     
     
     
  1. package bindingdemo; 
  2.   
  3. import javafx.beans.property.IntegerProperty; 
  4. import javafx.beans.property.SimpleIntegerProperty; 
  5. import javafx.beans.binding.NumberBinding; 
  6.   
  7. public class Main { 
  8.   
  9.     public static void main(String[] args) { 
  10.         IntegerProperty num1 = new SimpleIntegerProperty(1); 
  11.         IntegerProperty num2 = new SimpleIntegerProperty(2); 
  12.         NumberBinding sum = num1.add(num2); 
  13.         System.out.println(sum.getValue()); 
  14.         num1.set(2); 
  15.         System.out.println(sum.getValue()); 
  16.     } 

 

 

 

 This code binds the two dependencies, prints their sum, then changes the value of num1 and prints the sum again. (代码绑定了两个依赖,打印出它们的和,然后修改numl的值,再次打印出sum。)The results are "3" and "4", which proves that the binding is working.(结果是“3”和“4”,这证明了绑定是工作的。)

You could also use the Bindings class to do the same thing, as shown in Example 4.(你也可以使用绑定类做同样的事情,如例子4展示的。)

Example 4 Using the Bindings Class

   
   
   
   
  1. package bindingdemo; 
  2.   
  3. import javafx.beans.property.IntegerProperty; 
  4. import javafx.beans.property.SimpleIntegerProperty; 
  5. import javafx.beans.binding.NumberBinding; 
  6. import javafx.beans.binding.Bindings; 
  7.   
  8. public class Main { 
  9.   
  10.     public static void main(String[] args) { 
  11.        IntegerProperty num1 = new SimpleIntegerProperty(1); 
  12.        IntegerProperty num2 = new SimpleIntegerProperty(2); 
  13.        NumberBinding sum = Bindings.add(num1,num2); 
  14.        System.out.println(sum.getValue()); 
  15.        num1.setValue(2); 
  16.        System.err.println(sum.getValue()); 
  17.     } 

Example 5 combines the two approaches:(例子5结合了两个方法:)

Example 5 Combining Both Approaches

   
   
   
   
  1. package bindingdemo; 
  2.   
  3. import javafx.beans.property.IntegerProperty; 
  4. import javafx.beans.property.SimpleIntegerProperty; 
  5. import javafx.beans.binding.NumberBinding; 
  6. import javafx.beans.binding.Bindings; 
  7.   
  8. public class Main { 
  9.   
  10.     public static void main(String[] args) { 
  11.        IntegerProperty num1 = new SimpleIntegerProperty(1); 
  12.        IntegerProperty num2 = new SimpleIntegerProperty(2); 
  13.        IntegerProperty num3 = new SimpleIntegerProperty(3); 
  14.        IntegerProperty num4 = new SimpleIntegerProperty(4); 
  15.        NumberBinding total = 
  16.          Bindings.add(num1.multiply(num2),num3.multiply(num4)); 
  17.        System.out.println(total.getValue()); 
  18.        num1.setValue(2); 
  19.        System.err.println(total.getValue()); 
  20.     } 

Example 5 modifies the code to invoke the multiply method from the Fluent API, and add from the Bindings class. (例子5修改了代码,代码中设计了Fluent API中的multiply 方法,add方法来自绑定类。)You should also know that the High-Level API lets you mix types when defining arithmetic operations. (你要明白高级水平API让你在定义数学操作的时候可以混合类型。)The type of the result is defined by the same rules as the Java programming language:(结果的类型使用java中相同的规则定义。)

  1. If one of the operands is a double, the result is a double.(如果一个操作数是double,结果也是double。)

  2. If not and one of the operands is a float, the result is a float.(如果没有或者一个操作数是float,结果也是float。)

  3. If not and one of the operands is a long, the result is a long.(如果没有或者一个操作数是long,那么结果也是long。)

  4. The result is an integer otherwise.(其他情况结果是整型。)

The next section explores observability, and demonstrates how invalidation listeners differ from change listeners.(下面部分探索观察新,演示了失效监听器与改变监听器的差别。)

Exploring Observable, ObservableValue, InvalidationListener, and ChangeListener(探索Observable, ObservableValue, InvalidationListener, 和 ChangeListener)

The binding API defines a set of interfaces that enable objects to be notified when a value change or invalidation takes place. (绑定API定义了一系列的接口,这些接口可以在一个值修改或者失效发生的时候通知某些对象。)The Observable and ObservableValue interfaces fire the change notifications, and the InvalidationListener and ChangeListener interfaces receive them. (Observable 和ObservableValue接口触发通知,InvalidationListener 和ChangeListener 接受它们。)The difference between the two is that ObservableValue wraps a value and fires its changes to any registered ChangeListener, whereas Observable (which does not wrap a value) fires its changes to any registered InvalidationListener.(它们之间的差别是ObservableValue包装了值和触发任何注册了修改监听器的对象进行改变操作,而Observable(没有包装值)触发任何注册了无效监听器的对象。)

The JavaFX binding and property implementations all support lazy evaluation, which means that when a change occurs, the value is not immediately recomputed. (JavaFX绑定和属性实现了所有支持的懒惰执行,这意味这当一个改变发生的时候,值不会立刻重新计算。)Recomputation happens later, if and when the value is subsequently requested.(如果这个值随后被请求,重新计算将会发生。)

In Example 6, the bill total (a binding) will be marked as invalid the first time it detects a change in one of its dependencies.(在例子6中,账单总数(一个绑定)在第一次检测到它的依赖对象发生改变的时候标记为非法的。) However, the binding object will recalculate itself only if the total is actually requested again.(无论如何,当总数被确切地再次请求时,这个绑定对象才会重新计算它自己的值。)

Example 6 Using an InvalidationListener

   
   
   
   
  1. package bindingdemo; 
  2.   
  3. import javafx.beans.property.DoubleProperty; 
  4. import javafx.beans.property.SimpleDoubleProperty; 
  5. import javafx.beans.binding.NumberBinding; 
  6. import javafx.beans.binding.Bindings; 
  7. import javafx.beans.InvalidationListener; 
  8. import javafx.beans.Observable; 
  9.   
  10. class Bill { 
  11.   
  12.     // Define the property 
  13.     private DoubleProperty amountDue = new SimpleDoubleProperty(); 
  14.   
  15.     // Define a getter for the property's value 
  16.     public final double getAmountDue(){return amountDue.get();} 
  17.   
  18.     // Define a setter for the property's value 
  19.     public final void setAmountDue(double value){amountDue.set(value);} 
  20.   
  21.      // Define a getter for the property itself 
  22.     public DoubleProperty amountDueProperty() {return amountDue;} 
  23.   
  24.   
  25. public class Main { 
  26.   
  27.     public static void main(String[] args) { 
  28.   
  29.         Bill bill1 = new Bill(); 
  30.         Bill bill2 = new Bill(); 
  31.         Bill bill3 = new Bill(); 
  32.   
  33.         NumberBinding total = 
  34.           Bindings.add(bill1.amountDueProperty().add(bill2.amountDueProperty()), 
  35.               bill3.amountDueProperty()); 
  36.         total.addListener(new InvalidationListener() { 
  37.   
  38.         @Override public void invalidated(Observable o) { 
  39.                 System.out.println("The binding is now invalid."); 
  40.             } 
  41.         }); 
  42.  
  43.         // First call makes the binding invalid 
  44.         bill1.setAmountDue(200.00); 
  45.  
  46.         // The binding is now invalid 
  47.         bill2.setAmountDue(100.00); 
  48.         bill3.setAmountDue(75.00); 
  49.  
  50.         // Make the binding valid... 
  51.         System.out.println(total.getValue()); 
  52.  
  53.         // Make invalid...  
  54.         bill3.setAmountDue(150.00); 
  55.  
  56.         // Make valid... 
  57.         System.out.println(total.getValue()); 
  58.     } 

By changing the value of a single bill, the binding becomes invalid, and the invalidation listener will fire.(通过修改单个账单的值,绑定会变成非法的,失效监听器将会触发。) But if the binding is already invalid, the invalidation listener will not fire again, even if another bill changes. (如果绑定已经失效了,即使另一个账单也改变了失效监听器将不会重新触发)(In Example 6, invoking total.getValue() moves the binding from invalid to valid.) (在例子6中,提到的total.getValue()使绑定从失效到有效。)We know this because a subsequent change to any bill in the dependency list will cause the invalidation listener to fire again. This would not happen if the binding was still invalid.(我们知道这是因为任何在依赖列表中的账单的后续改变都会重新出发失效监听器。如果绑定还是无效的,这将不会发生。)

Note that registering a ChangeListener will enforce eager computation, even if the implementation of the ObservableValue supports lazy evaluation. (注意注册一个改变监听器将会执行饥渴计算,即使ObservableValue支持延迟执行。)For a lazily evaluated value, it is not possible to know if an invalid value really has changed until it is recomputed.(对于懒惰执行的值,这是不可能知道在没有重新计算之前失效的值是否已经改变了。) For this reason, generating change events requires eager evaluation, while invalidation events can be generated for both eager and lazy implementations.(因为这个原因,生成改变事件要求饥渴执行,失效事件能够实现饥渴和懒惰实现。)

Using the Low-Level Binding API(使用低水平绑定API)

If the High-Level API is not enough to satisfy your requirements, you can always use the Low-Level API instead. (如果高水平API不能够满足你的要求,你可以使用低水平API。)The Low-Level API is for developers who require more flexibility (or better performance) than that offered by the High-Level API.(低水平API是为那些需要比高级API更多灵活性或者更好表现的程序员的。)

Example 7 shows a basic example of using the Low-Level API.(例子7展示了使用低级水平API的例子。)

Example 7 Using the Low-Level API

    
    
    
    
  1. package bindingdemo; 
  2.   
  3. import javafx.beans.property.DoubleProperty; 
  4. import javafx.beans.property.SimpleDoubleProperty; 
  5. import javafx.beans.binding.DoubleBinding; 
  6.   
  7. public class Main { 
  8.   
  9.     public static void main(String[] args) { 
  10.   
  11.         final DoubleProperty a = new SimpleDoubleProperty(1); 
  12.         final DoubleProperty b = new SimpleDoubleProperty(2); 
  13.         final DoubleProperty c = new SimpleDoubleProperty(3); 
  14.         final DoubleProperty d = new SimpleDoubleProperty(4); 
  15.   
  16.         DoubleBinding db = new DoubleBinding() { 
  17.   
  18.             { 
  19.                 super.bind(a, b, c, d); 
  20.             } 
  21.   
  22.             @Override 
  23.             protected double computeValue() { 
  24.                 return (a.get() * b.get()) + (c.get() * d.get()); 
  25.             } 
  26.         }; 
  27.   
  28.         System.out.println(db.get()); 
  29.         b.set(3); 
  30.         System.out.println(db.get()); 
  31.     } 

Using the Low-Level API involves extending one of the binding classes and overriding itscomputeValue() method to return the current value of the binding. (使用低水平API设计继承一个绑定类和和重写itscomputeValue方法来返回绑定现在的值。)Example 7 does this with a custom subclass of DoubleBinding. (例子7使用自定义子类DoubleBinding。)The invocation of super.bind() passes the dependencies up to DoubleBinding so that the default invalidation behavior is retained.(调用super.bind()方法传递依赖给DoubleBinding使默认的失效行为能够保留。) It is generally not necessary for you to check if the binding is invalid; this behavior is provided for you by the base class.(这对你来说检查绑定是否失效是没有必要的;这个行为是由基类提供给你的。)

You now know enough information to begin using the Low-Level API.(你已经知道了开始使用低水平API的足够信息。)