JavaFX 中的属性与基础数据(int,double,boolean 等)一一对应,可以认为是对基础数据进行了包装,相较基础数据,属性最大的区别就是属性对 “失效、修改” 等事件可以做出反馈。
基础数据与对应的属性示例:
String:StringProperty, SimpleStringProperty, ReadOnlyStringProperty
int: IntegerProperty, SimpleIntegerProperty, ReadOnlyIntegerProprety
每个属性都继承了 Observable,ObservableValue 接口,必须实现 void addListener(InvalidationListener listener) 方法和 void addListener(ChangeListener super T> listener) 方法(remove 方法是删除监听器)。
// Invalidate event void addListener(InvalidationListener listener) // 接口 interface InvalidationListener { void invalidated(Observable observable); } // Change event void addListener(ChangeListener super T> listener) // 接口 interface ChangeListener{ void changed(ObservableValue extends T> observable, T oldValue, T newValue); }
在此分别对 Invalidate 和 Change 进行测试。
注意Invalidation event和Changed event的区别: change events and invalidation events. A change event indicates that the value has changed (see note 2 in "Implementation Requirements"). An invalidation event is generated if the current value is not valid anymore. This distinction becomes important if the ObservableValue
supports lazy evaluation, because for a lazily evaluated value one does not 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 eager and lazy implementations.(转载JavaFX17中接口ObservableValue介绍)。
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-------------------\n");
// At this point counter property is invalid and further changes to its value will not
// generate invalidation events
System.out.println("Before 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-------------------\n");
// 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("Before changing the counter value-3--------------------");
counter.set(102);
System.out.println("After changing the counter value-3-------------------\n");
// Try to set a different value
System.out.println("Before changing the counter value-4--------------------");
counter.set(103);
System.out.println("After changing the counter value-4--------------------\n");
}
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--------------------
Process finished with exit code 0
每次数据的变化都会触发 invalidate 事件。
Changed事件:
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 property Property
* @param newNumber The new value
* @param counter The number of count
* @return
* @throws
*********************************************************************************************/
private static void fireChangedEvent(IntegerProperty property, int newNumber, int counter) {
System.out.println("\nBefore changing the counter value-" + counter);
// Set new value, fire change event
property.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 property 触发事件的实例,在这里是changedFunction方法中的实例property。触发事件会自动判定传入
* 的oldValue和newValue
* @param oldValue 触发事件前的值
* @param newValue 触发事件后的值
* @return
* @throws
*********************************************************************************************/
private static void changed(ObservableValue extends Number> property,
Number oldValue,
Number newValue) {
// Observable instance's value is the new value
System.out.println("Property's value : " + property.getValue().intValue());
System.out.println("Number changed : ");
System.out.println("Old = " + oldValue + ", New = " + newValue);
}
}
// 运行结果
Before changing the counter value-1
Property's value : 101
Number changed :
Old = 100, New = 101
After changing the counter value-1
Before changing the counter value-2
Property's value : 102
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
Property's value : 104
Number changed :
Old = 102, New = 104
After changing the counter value-4
每次数据进行修改就会出发changed事件。
如果一个属性同时注册监听invalidated事件和chagned事件,那会怎么处理,处理顺序是什么?
package learnjavafx8.ch02;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
/**************************************************************************************************
* @copyright 2003-2022
* @package learnjavafx8.ch02
* @file ChangeAndInvalidationTest.java
* @date 2020/5/3 12:10
* @author qiao wei
* @version 1.0
* @brief 测试ChangeListener和InvalidationListener方法的区别。Invalidation事件触发在Change事件之前
* @history
*************************************************************************************************/
public class ChangeAndInvalidationTest {
public static void main(String[] args) {
IntegerProperty counter = new SimpleIntegerProperty(100);
// Register a change listener to the property
counter.addListener(ChangeAndInvalidationTest::changed);
// Register an invalidation listener to the property
counter.addListener(ChangeAndInvalidationTest::invalidated);
printDetails(counter, 101, 1);
printDetails(counter, 102, 2);
// No change value, no validate no change, skip to call invalidated and changed method
printDetails(counter, 102, 3);
printDetails(counter, 104, 4);
}
/**********************************************************************************************
* @class ChangeAndInvalidationTest
* @date 2023-01-30 22:08
* @author qiao wei
* @version 1.0
* @brief Invalidate event
* @param observable Observer
* @return
* @throws
*********************************************************************************************/
public static void invalidated(Observable observable) {
System.out.println("Counter is in invalid status");
}
/**********************************************************************************************
* @class ChangeAndInvalidationTest
* @date 2023-01-24 15:44
* @author qiao wei
* @version 1.0
* @brief Change event
* @param observableValue Observer
* @param oldValue Old value
* @param newValue New value
* @return
* @throws
*********************************************************************************************/
public static void changed(ObservableValue extends Number> observableValue,
Number oldValue,
Number newValue) {
System.out.println("Counter is changed: ");
System.out.println("old = " + oldValue + ", new = " + newValue);
}
/**********************************************************************************************
* @class ChangeAndInvalidationTest
* @date 2023-01-24 15:48
* @author qiao wei
* @version 1.0
* @brief Print details
* @param
* @return
* @throws
*********************************************************************************************/
private static void printDetails(IntegerProperty property, int newNumber, int counter) {
System.out.println("\nBefore setting the counter value:" + counter);
// Reset property value and fire invalidate event first and change event second
property.set(newNumber);
System.out.println("After setting the counter value:" + counter);
}
}
运行结果:
Before setting the counter value:1
Counter is in invalid status
Counter is changed:
old = 100, new = 101
After setting the counter value:1
Before setting the counter value:2
Counter is in invalid status
Counter is changed:
old = 101, new = 102
After setting the counter value:2
Before setting the counter value:3
After setting the counter value:3
Before setting the counter value:4
Counter is in invalid status
Counter is changed:
old = 102, new = 104
After setting the counter value:4
Process finished with exit code 0
从运行结果可以得出都是先触发 invalidated方法,后 changed方法。invalidted在Observable接口定义,是ObservableValue的超类接口(changed 方法在此接口中)。