J1003.JavaFX屬性和綁定01——簡單對象


JavaBean擴展

為了實現基於JavaBean的屬性、綁定以及事件機制,JavaFX對JavaBean進行了擴展,JavaBean不再是POJO,顯得更加“重量級”一些。

 

JavaFX屬性

JavaFX為Java通用包裝類提供了通用的屬性包裝類,以實現事件監聽、數據綁定等功能。如下表:

所有這些類都是Observable接口的實現類,以上只是示例了部分,詳細類見javafx.beans.property包下。

JavaFX中的Property體系沒有實現Serializable接口,所以無法跨JVM傳遞。也就是說,應用服務器無法向客戶端返回這類信息;客戶端也無法將這類信息傳遞給應用服務器。這給我們開發B/S應用系統造成了困擾。

由於需要進行值監聽等操作,所以JavaFX的Property對空屬性比較敏感,我們在寫代碼時要特別關注。

 

JavaFX Property對空屬性的敏感

如果Property沒有初始化,則對其操作時會報出空指針。

package com.lirong.javafx.demo.j1003;

 

import javafx.beans.property.StringProperty;

 

public class DemoFXBean {

 

/**

* 編碼

*/

private StringProperty test_code;

 

/**

* 名稱

*/

private StringProperty test_name;

 

public String getTest_code() {

 

return test_code.get();

}

 

public StringProperty test_codeProperty() {

 

return test_code;

}

 

public void setTest_code(String test_code) {

 

this.test_code.set(test_code);

}

 

public String getTest_name() {

 

return test_name.get();

}

 

public StringProperty test_nameProperty() {

 

return test_name;

}

 

public void setTest_name(String test_name) {

 

this.test_name.set(test_name);

}

}

 

 

我們可以看到,JavaFX Bean中,每個屬性都會有三個方法

1、getter值的方法,如getTest_code();

2、setter值的方法,如setTest_code(String test_code);

3、獲取屬性的方法,如test_codeProperty();

 

當我們使用以下代碼進行測試時:

package com.lirong.javafx.demo.j1003;

 

public class TestFXBean {

 

public static void main(String[] args) {

 

DemoFXBean demoFXBean = new DemoFXBean();

demoFXBean.setTest_code("TestCode");

}

}

 

控制台打印如下錯誤信息:

 

從DemoFXBean的代碼可以看出,對屬性操作的三個方法,都是基於Property進行的。所以如果不初始化Property,get、set時,就會發生空指針異常。

 

一個簡單的JavaFX Bean

當Bean中某個字段值發生變化時,打印信息並修改其它值。我們把DemoFXBean修改一下:

package com.lirong.javafx.demo.j1003;

 

import javafx.beans.property.SimpleStringProperty;

import javafx.beans.property.StringProperty;

 

public class DemoFXBean {

 

/**

* 編碼

*/

private StringProperty test_code = new SimpleStringProperty();

 

/**

* 名稱

*/

private StringProperty test_name = new SimpleStringProperty();

 

public DemoFXBean() {

 

super();

initListener();

}

 

private void initListener() {

 

// 監聽test_code屬性的變化,同時修改其它屬性值

test_codeProperty().addListener((observable, oldValue, newValue) -> {

 

System.out.println(String.format("test_code changed. oldValue=%s, newValue=%s", oldValue, newValue));

// 設置其它屬性值

setTest_name(String.format("%s 的名稱", newValue));

});

 

// 監聽test_name屬性的變化

test_nameProperty().addListener((observable, oldValue, newValue) -> {

 

System.out.println(String.format("test_name changed. oldValue=%s, newValue=%s", oldValue, newValue));

});

}

 

public String getTest_code() {

 

return test_code.get();

}

 

public StringProperty test_codeProperty() {

 

return test_code;

}

 

public void setTest_code(String test_code) {

 

this.test_code.set(test_code);

}

 

public String getTest_name() {

 

return test_name.get();

}

 

public StringProperty test_nameProperty() {

 

return test_name;

}

 

public void setTest_name(String test_name) {

 

this.test_name.set(test_name);

}

}

 

主要修改了以下內容:

1、初始化Property;

2、增加監聽器;

再運行TestFXBean測試類時,控制台打印如下信息:

 

可以看到,兩個監聽器都產生了預期的動作。

 

JavaBean的屬性監聽機制

如前所述,JavaFX的Property由於沒有實現序列化,所以無法跨JVM傳遞。所以我們要有一種JavaFX Bean和JavaBean之間的包裝、轉換機制,使Bean能夠跨JVM。這種機制是我們后續在B/S框架中使用JavaFX的必要准備。

我們的方法是:改造javaBean,通過改造后的JavaBean包裝生成JavaFX Bean,並使JavaBean和JavaFX Bean之間的屬性實現雙向聯動。

增加一個DemoJavaBean,為其實現屬性監聽

package com.lirong.javafx.demo.j1003;

 

import java.beans.PropertyChangeListener;

import java.beans.PropertyChangeSupport;

import java.io.Serializable;

 

public class DemoJavaBean implements Serializable {

 

private static final long serialVersionUID = -6499105304636177551L;

 

protected final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

 

/**

* 編碼

*/

private String test_code;

 

/**

* 名稱

*/

private String test_name;

 

public String getTest_code() {

 

return test_code;

}

 

public void setTest_code(String test_code) {

 

final String oldValue = this.test_code;

this.test_code = test_code;

propertyChangeSupport.firePropertyChange("test_code", oldValue, this.test_code);

}

 

public String getTest_name() {

 

return test_name;

}

 

public void setTest_name(String test_name) {

 

final String oldValue = this.test_name;

this.test_name = test_name;

propertyChangeSupport.firePropertyChange("test_name", oldValue, this.test_name);

}

 

public void addPropertyChangeListener(PropertyChangeListener listener) {

 

propertyChangeSupport.addPropertyChangeListener(listener);

}

 

public void removePropertyChangeListener(PropertyChangeListener listener) {

 

propertyChangeSupport.removePropertyChangeListener(listener);

}

}

 

需要注意的是:

1、為DemoJavaBean增加了PropertyChangeSupport屬性,並增加了兩個相關方法addPropertyChangeListener和removePropertyChangeListener;

2、在setter中,產生屬性變化事件,並傳遞修改前、修改后的值;

3、該類實現了序列化接口;

 

增加DemoJavaFXBean類:

package com.lirong.javafx.demo.j1003;

 

import javafx.beans.property.SimpleStringProperty;

import javafx.beans.property.StringProperty;

import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;

 

public class DemoJavaFXBean {

 

private DemoJavaBean javaBean;

 

/**

* 編碼

*/

private StringProperty test_code = new SimpleStringProperty();

 

/**

* 名稱

*/

private StringProperty test_name = new SimpleStringProperty();

 

/**

* DemoJavaFXBean的構造器,必須傳入一個DemoJavaBean,用於初始化屬性

*

* @param javaBean

*/

public DemoJavaFXBean(DemoJavaBean javaBean) {

 

super();

this.javaBean = javaBean;

initProperty();

}

 

/**

* 通過 DemoJavaBean 初始化 DemoJavaFXBean 的屬性

*/

private void initProperty() {

 

// 根據JavaBean初始化JavaFX Bean的屬性

try {

/* 編碼 */

test_code = JavaBeanStringPropertyBuilder.create().bean(this.javaBean).name("test_code").build();

/* 名稱 */

test_name = JavaBeanStringPropertyBuilder.create().bean(this.javaBean).name("test_name").build();

} catch (Exception ex) {

throw new RuntimeException(ex);

}

}

 

public DemoJavaBean getJavaBean() {

 

return javaBean;

}

 

public void setJavaBean(DemoJavaBean javaBean) {

 

this.javaBean = javaBean;

// 設置JavaBean后,需要重新初始化JavaFX Bean

initProperty();

}

 

public String getTest_code() {

 

return test_code.get();

}

 

public StringProperty test_codeProperty() {

 

return test_code;

}

 

public void setTest_code(String test_code) {

 

this.test_code.set(test_code);

}

 

public String getTest_name() {

 

return test_name.get();

}

 

public StringProperty test_nameProperty() {

 

return test_name;

}

 

public void setTest_name(String test_name) {

 

this.test_name.set(test_name);

}

}

 

需要注意的是:

1、DemoJavaFXBean的構造器,授受一個DemoJavaBean,以進行本類的屬性初始化;

2、通過initProperty方法進行屬性初始化;

3、該類沒有實現序列化接口;

 

測試類TestJavaBeanFX,主要用於測試屬性修改是否在JavaFX Bean和JavaBean之間自動聯動:

package com.lirong.javafx.demo.j1003;

 

public class TestJavaBeanFX {

 

public static void main(String[] args) {

 

DemoJavaBean demoJavaBean = new DemoJavaBean();

 

DemoJavaFXBean demoJavaFXBean = new DemoJavaFXBean(demoJavaBean);

 

final String strFormatter = "JavaFX Bean PropertyName=%s, PropertyValue=%s; Java Bean PropertyName=%s, PropertyValue=%s";

// 修改Java Bean的屬性,查看JavaFX Bean相應的屬性是否同步修改了

demoJavaFXBean.getJavaBean().setTest_code("TestCode");

System.out.println(String.format(strFormatter, "Code", demoJavaFXBean.getTest_code(), "Code", demoJavaFXBean.getJavaBean().getTest_code()));

 

// 修改JavaFX Bean的屬性,查看Java Bean相應的屬性是否同步修改了

demoJavaFXBean.setTest_name("TestName");

System.out.println(String.format(strFormatter, "Name", demoJavaFXBean.getTest_name(), "Name", demoJavaFXBean.getJavaBean().getTest_name()));

}

}

 

控制台輸出以下信息:

 

我們可以看到,修改JavaFX Bean或Java Bean的屬性值,將同步兩個Bean的屬性值。

需要注意的是:

1、必須為JavaBean實現屬性變化監聽事件機制;

2、通過JavaBean構造JavaFX Bean,並實現屬性初始化;

通過本例也可以進一步看出:在JavaFX屬性機制中,屬性不可為空,屬性值可以為空。我們在TestJavaBeanFX類中實例化DemoJavaBean時,沒有為它的任何屬性賦值。但在初始化DemoJavaFXBean時,通過JavaFX的PropertyBuilder產生屬性值為空的非空屬性。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM