现在的编程体系,都是面向对象编程,其中分为两个部分:方法 + 字段,其中字段设置为 private,通过方法读取字段,外界程序不直接对字段操作。
在程序运行过程中,经常会对对象的字段进行更新,所以如何将更新的字段信息通知,这是要注意的问题。在 JavaFX 中,可以使用属性 Property 替代字段,使用属性的监听器通知所有监听者,在数据修改时通知监听者更新数据。每个基本类型字段的属性接口:***Property,对应的属性实例有 2 种,分别是 Simple***Property 和 ReadOnly***Wrapper,第 2 个是属性的包装器。
属性示例,属性构成 Book 类中的字段:
package learnjavafx8.ch02;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**************************************************************************************************
@copyright 2003-2022
@package learnjavafx8.ch02
@file Book.java
@date 2021/7/10 12:22
@author qiao wei
@version 1.0
@brief Book. Test properties
@history
**************************************************************************************************/
public class Book {
/**********************************************************************************************
* @class Book
* @date 2022-12-24 15:11
* @author qiao wei
* @version 1.0
* @brief Default constructor
* @param
* @return
* @throws
*********************************************************************************************/
public Book() {
this("Unknown", 0.0d, "Unknown");
}
/**********************************************************************************************
* @class Book
* @date 2022-12-24 15:10
* @author qiao wei
* @version 1.0
* @brief Constructor
* @param
* @return
* @throws
*********************************************************************************************/
public Book(String title, double price, String ISBN) {
this.titleProperty = new SimpleStringProperty(this, "title", title);
this.priceProperty = new SimpleDoubleProperty(this, "price", price);
// Property wrapper. It can read/write
this.ISBN = new ReadOnlyStringWrapper(this, "ISBN", ISBN);
}
/**********************************************************************************************
@class Book
@date 2022/1/16 22:09
@author qiao wei
@version 1.0
@brief Get title Property
@return The title property
**********************************************************************************************/
public final StringProperty getTitleProperty() {
return titleProperty;
}
/**********************************************************************************************
@class Book
@date 2022/1/16 22:09
@author qiao wei
@version 1.0
@brief Get book title
@return The book title
**********************************************************************************************/
public final String getTitle() {
return titleProperty.get();
}
public final void setTitle(String title) {
titleProperty.set(title);
}
/**********************************************************************************************
@class Book
@date 2022/1/16 22:10
@author qiao wei
@version 1.0
@brief Get the book price property
@return The book price property
**********************************************************************************************/
public final DoubleProperty getPriceProperty() {
return priceProperty;
}
/**********************************************************************************************
@class Book
@date 2022/1/16 22:13
@author qiao wei
@version 1.0
@brief Get the book price
@return The book price
**********************************************************************************************/
public double getPrice() {
return priceProperty.get();
}
public final void setPrice(double price) {
priceProperty.set(price);
}
/**********************************************************************************************
@class Book
@date 2022/1/16 22:20
@author qiao wei
@version 1.0
@brief 获取只读型ISBN属性
@return ISBN属性
**********************************************************************************************/
public final ReadOnlyStringProperty getISBNProperty() {
return ISBN.getReadOnlyProperty();
}
/**********************************************************************************************
@class Book
@date 2022/1/16 22:21
@author qiao wei
@version 1.0
@brief 获取只读型ISBN数据
@return ISBN数据
**********************************************************************************************/
public final String getISBN() {
return ISBN.get();
}
/**********************************************************************************************
@date 2022/1/16 22:06
@author qiao wei
@brief Book title property
**********************************************************************************************/
private StringProperty titleProperty;
/**********************************************************************************************
@date 2022/1/16 22:06
@author qiao wei
@brief Price property
**********************************************************************************************/
private DoubleProperty priceProperty;
/**********************************************************************************************
@date 2022/1/16 22:06
@author qiao wei
@brief Book ISBN. Read only field
**********************************************************************************************/
private ReadOnlyStringWrapper ISBN;
}
测试 Book 类:
package learnjavafx8.ch02;
import javafx.beans.property.ReadOnlyProperty;
//import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**************************************************************************************************
@copyright 2003-2022
@package learnjavafx8.ch02
@file BookTest.java
@date 2022/1/26 16:21
@author qiao wei
@version 1.0
@brief Test JavaFX property
@history
**************************************************************************************************/
public class BookTest {
public static void main(String[] args) {
Book book = new Book("Harnessing JavaFX", 9.99d, "0123456789");
System.out.println("After creating the Book object...");
BookTest.printDetails(book.getTitleProperty());
// printDetails(book.getPriceProperty());
// printDetails(book.getISBNProperty());
// book.setTitle("JavaFX 8.0");
/** book.setPrice(22.49d);*/
StringProperty property = book.getTitleProperty();
property.set("Test");
System.out.println("\nTitle changed to JavaFX 8.0, and Price is 22.49" );
System.out.println("After changing the Book object...\n");
printDetails(book.getTitleProperty());
// printDetails(book.getPriceProperty());
// printDetails(book.getISBNProperty());
// TestClass tc = new TestClass("Harnessing JavaFX", 9.99d, "0123456789");
}
/**********************************************************************************************
@class BookTest
@date 2022/1/26 16:21
@author qiao wei
@version 1.0
@brief Print book's details
@param property book property
**********************************************************************************************/
private static void printDetails(ReadOnlyProperty<?> property) {
// Get the object's property implementing from the property interface
String name = property.getName();
Object bean = property.getBean();
Object value = property.getValue();
String beanClassName = (null == bean) ? "null" : bean.getClass().getSimpleName();
String propClassName = property.getClass().getSimpleName();
System.out.print(propClassName);
System.out.print("[Name = " + name);
System.out.print(", Bean Class = " + beanClassName);
System.out.println(", Value = " + value + "]");
System.out.println("propClass = " + propClassName);
}
}
运行结果:
After creating the Book object...
SimpleStringProperty[Name = title, Bean Class = Book, Value = Harnessing JavaFX]
propClass = SimpleStringProperty
Title changed to JavaFX 8.0, and Price is 22.49
After changing the Book object...
SimpleStringProperty[Name = title, Bean Class = Book, Value = Test]
propClass = SimpleStringProperty
从结果上看,使用属性并没有带来什么新的特性,相较于 String,double 步骤多,反而麻烦。但是属性有 2 种事件监听,分别是失效事件 invalidated 和改变事件 changed,分别可以对数据的改变做出反应,通知数据的监听者,这是 String 等类型数据没有的,数据更新注意两者的区别。
// 无效事件方法,继承自Observable接口
void addListener(InvalidationListener listener)
// 改变事件方法,继承自ObservableValue接口
void addListener(ChangeListener<? super T> listener)
invalidated 事件例子:
package learnjavafx8.ch02;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
/**************************************************************************************************
* @copyright 2003-2022
* @package learnjavafx8.ch02
* @file InvalidationTest.java
* @date 2022/11/19 11:30
* @author qiao wei
* @version 1.0
* @brief
* @history
*************************************************************************************************/
public class InvalidationTest {
public static void main(String[] args) {
IntegerProperty counter = new SimpleIntegerProperty(100);
// Register an invalidation listener to counter property
counter.addListener(InvalidationTest::invalidated);
// Set the same value 100
System.out.println("Before invalidating the counterProperty value-1");
// 数据从100到100,没有触发invalidation event
counter.set(100);
System.out.println("Before invalidating the counterProperty value-1");
// At this point counter property is invalid and further changes to its value will not
// generate invalidation events
System.out.println("\nBefore changing the counter value-2");
counter.set(102);
System.out.println("counter's value is not changed, invalidating event is ");
System.out.println("After changing the counter value-2");
// Make the counter property valid by calling its get() method
int value = counter.get();
System.out.println("Counter value = " + value);
// At this point counter property is valid and further changes to its value will generate
// invalidation events.
// Try to set the same value
System.out.println("\nBefore changing the counter value-3");
counter.set(102);
System.out.println("After changing the counter value-3");
// Try to set a different value
System.out.println("\nBefore changing the counter value-4");
counter.set(103);
System.out.println("After changing the counter value-4");
}
public static void invalidated(Observable property) {
System.out.println("Counter is invalid. The invalidation event is fired.");
}
}
运行结果:
Before invalidating the counterProperty value-1
Before invalidating the counterProperty value-1
Before changing the counter value-2
Counter is invalid. The invalidation event is fired.
counter's value is not changed, invalidating event is
After changing the counter value-2
Counter value = 102
Before changing the counter value-3
After changing the counter value-3
Before changing the counter value-4
Counter is invalid. The invalidation event is fired.
After changing the counter value-4
change 事件:
package learnjavafx8.ch02;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
/**************************************************************************************************
* @copyright 2003-2022
* @package learnjavafx8.ch02
* @file ChangeTest.java
* @date 2020/5/2 18:51
* @author qiao wei
* @version 1.0
* @brief Test change event declared from ObservableValue interface
* @history
*************************************************************************************************/
public class ChangeTest {
public static void main(String[] args) {
changedFunction();
}
private static void changedFunction() {
IntegerProperty property = new SimpleIntegerProperty(100);
// Register change listener
property.addListener(ChangeTest::changed);
fireChangedEvent(property, 101, 1);
fireChangedEvent(property, 102, 2);
// New value is the same with old value, the change event is not fired
fireChangedEvent(property, 102, 3);
fireChangedEvent(property, 104, 4);
}
/**********************************************************************************************
* @class ChangeTest
* @date 2023-01-19 12:59
* @author qiao wei
* @version 1.0
* @brief Fire change event by reset property
* @param
* @return
* @throws
*********************************************************************************************/
private static void fireChangedEvent(IntegerProperty prop, int newNumber, int counter) {
System.out.println("\nBefore changing the counter value-" + counter);
// Set new value
prop.set(newNumber);
System.out.println("After changing the counter value-" + counter);
}
/**********************************************************************************************
* @class ChangeTest
* @date 2022/10/22 16:33
* @author qiao wei
* @version 1.0
* @brief 修改事件
* @param prop 触发事件的实例,在这里是changedFunction方法中的实例property
* @param oldValue 触发事件前的值
* @param newValue 触发事件后的值
* @return
* @throws
*********************************************************************************************/
private static void changed(ObservableValue<? extends Number> prop,
Number oldValue,
Number newValue) {
System.out.println("Number changed : ");
System.out.println("Old = " + oldValue + ", New = " + newValue);
}
}
运行结果:
Before changing the counter value-1
Number changed :
Old = 100, New = 101
After changing the counter value-1
Before changing the counter value-2
Number changed :
Old = 101, New = 102
After changing the counter value-2
Before changing the counter value-3
After changing the counter value-3
Before changing the counter value-4
Number changed :
Old = 102, New = 104
After changing the counter value-4
Process finished with exit code 0