原文地址: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
高級API:提供了一種簡單的途徑來建立最多見的綁定(Binding)。它的語法很容易學習和使用,特別是在那些提供代碼補全的編程環境裏,好比NetBeans IDE。工具
低級API:提供了額外的靈活性,能夠由高級開發人員在高級API不夠用的狀況下使用。低級API是爲快速執行和小內存佔用而設計的。學習
本教程的其他部分描述這些api,並提供了代碼示例,你能夠編譯和運行它。開發工具
正如在概述裏提到的,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(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語言的約定相同。
若是其中一個操做數是double類型,則結果也爲double類型。
不然若其中一個是float類型,則結果爲float類型。
不然若其中一個是long類型,則結果爲long類型。
不然結果爲int類型。
下一節將探索可觀察性(observability),並經過示例展現invalidation listeners和change listeners的差異。
綁定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提供更多的靈活性。例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值被再次請求也能夠被當即觸發了。