Using JavaFX Properties and Binding

 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的足夠信息。)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章