ObservableList, ObservableSet和ObservableMap可以封装成Property,支持binding。
1. ListProperty
ListProperty是ObservableList的封装,数据存储在传入的一个ObservableList。ListProperty实现了ObservableList接口,可以直接调用ObservableList方法,来操作封装的ObservableList对象。
1.1 创建ListProperty
ListProperty必须要接受一个ObservableList才能使用,向一个空的ListProperty添加元素,会抛出异常。使用set方法,给一个空ListProperty传递ObservableList,或构造ListProperty时传递ObservableList。
// Create an empty ListProperty.
ListProperty<String> listProperty = new SimpleListProperty<>();
// Throw exception.
// listProperty.add("Hello");
listProperty.set(FXCollections.observableArrayList());
listProperty.add("Hello");
// Pass an ObservableList in constructor.
ListProperty<String> listProperty2 = new SimpleListProperty<>(
FXCollections.observableArrayList());
listProperty.add("Hello");
1.2 监听Invalidation事件
- 封装的ObservableList中的元素,有任何变化,都会触发Invalidation event。
- 重新set ListProperty中的ObservableList时,遵循普通Property规则,只有调用get方法后,再次set才会触发Invadation event。
ListProperty<String> listProperty = new SimpleListProperty<>(
FXCollections.observableArrayList());
listProperty.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
System.out.println("ListProperty is invalid!");
}
});
listProperty.add("one");
listProperty.add("two");
listProperty.get();
listProperty.remove("two");
System.out.println("........................");
listProperty.set(FXCollections.observableArrayList());
listProperty.set(FXCollections.observableArrayList());
listProperty.set(FXCollections.observableArrayList());
listProperty.get();
listProperty.set(FXCollections.observableArrayList());
// Output:
ListProperty is invalid!
ListProperty is invalid!
ListProperty is invalid!
........................
ListProperty is invalid!
ListProperty is invalid!
1.3 监听ChangeListener
- 封装的ObservableList内容变化时,会触发ChangeLister。因为只是内容变化,本身封装的ObservableList对象没变,所以oldValue和newValue相等。
- 重新set ListProperty的ObservableList时,和普通Property一样,oldValue是之前的ObservableList,newValue是新的ObservableList。
ListProperty<String> listProperty = new SimpleListProperty<>(
FXCollections.observableArrayList());
listProperty.addListener(new ChangeListener<ObservableList<String>>() {
@Override
public void changed(ObservableValue<? extends ObservableList<String>> observable,
ObservableList<String> oldValue, ObservableList<String> newValue) {
System.out.println("oldValue == newValue: " + (oldValue == newValue));
}
});
listProperty.add("one");
listProperty.add("remove");
System.out.println("***************************");
listProperty.set(FXCollections.observableArrayList());
listProperty.set(FXCollections.observableArrayList());
// Output:
oldValue == newValue: true
oldValue == newValue: true
***************************
oldValue == newValue: false
oldValue == newValue: false
1.4 监听ListChangeListener
ListProperty本身实现了ObservableList,又封装新的ObservableList对象。当add, remove, sort,set等改变ObservableList内容时,会触发ListChangeListener。这个跟普通ObservableList没有区别,下面只是一个小Exmaple。
ListProperty<String> listProperty = new SimpleListProperty<>(
FXCollections.observableArrayList());
listProperty.addListener(new ListChangeListener<String>() {
@Override
public void onChanged(ListChangeListener.Change<? extends String> change) {
while (change.next()) {
if (change.wasAdded()) {
System.out.println("Added size: " + change.getAddedSize());
}
}
}
});
listProperty.add("one");
listProperty.addAll(Lists.newArrayList("two", "three"));
// Output:
Added size: 1
Added size: 2
1.5 ListProperty使用Binding
1.5.1 绑定ListProperty的sizeProperty和emptyProperty
- size property是一个ReadOnlyIntegerProperty
- empty property是一个ReadOnlyBooleanProperty
就是普通的Property,想怎么绑定就怎么绑定。
ListProperty<String> listProperty = new SimpleListProperty<>(
FXCollections.observableArrayList());
StringExpression listDesc = new SimpleStringProperty("Size: ").concat(listProperty.sizeProperty())
.concat(", Empty: ").concat(listProperty.emptyProperty());
System.out.println(listDesc.get());
listProperty.add("one");
System.out.println(listDesc.get());
// Output:
Size: 0, Empty: true
Size: 1, Empty: false
1.5.2 绑定封装的ObservableList的引用
当ListProperty调用bind绑定另一个ListProperty时,封装的ObservableList与绑定的ListProperty的ObservableList相同。
ObservableList<String> listPropRef1 = FXCollections.observableArrayList();
ListProperty<String> listProp1 = new SimpleListProperty<>(listPropRef1);
ObservableList<String> listPropRef2 = FXCollections.observableArrayList();
ListProperty<String> listProp2 = new SimpleListProperty<>(listPropRef2);
System.out.println("listProp1.get() == listProp2 :" + (listProp1.get() == listPropRef2));
listProp1.bind(listProp2);
System.out.println("listProp1.get() == listProp2 :" + (listProp1.get() == listPropRef2));
// Output:
listProp1.get() == listProp2 :false
listProp1.get() == listProp2 :true
可以使用bindBidrectional()进行双向绑定。
1.5.3 绑定封装的ObservableList的内容
当使用bindContent绑定时,只是内容相同,它们封装的ObservableList是不一样的。
注意:当进行内容绑定时,仍然可以修改ListProperty中的元素(add, remove, clear 等),但会导致元素内容和绑定的不一样,造成不一致。绑定后,最好不要再修改元素内容。
ListProperty<String> listProp1 = new SimpleListProperty<>(FXCollections.observableArrayList());
ListProperty<String> listProp2 = new SimpleListProperty<>(FXCollections.observableArrayList());
listProp2.add("one");
System.out.println("listProp1: " + listProp1.getValue());
listProp1.bindContent(listProp2);
System.out.println("listProp1: " + listProp1.getValue());
listProp2.add("two");
System.out.println("listProp1: " + listProp1.getValue());
System.out.println("****************************");
// Change the content after bind content.
listProp1.add("three");
System.out.println("listProp1: " + listProp1.getValue());
System.out.println("listProp2: " + listProp2.getValue());
// Output:
listProp1: []
listProp1: [one]
listProp1: [one, two]
****************************
listProp1: [one, two, three]
listProp2: [one, two]
也可以使用bindBidrectional()双向绑定内容,任何一个ListProperty元素改变时,都进行同步。
ListProperty<String> listProp1 = new SimpleListProperty<>(FXCollections.observableArrayList());
ListProperty<String> listProp2 = new SimpleListProperty<>(FXCollections.observableArrayList());
listProp2.add("one");
listProp1.bindBidirectional(listProp2);
System.out.println("listProp1: " + listProp1.getValue());
listProp1.add("two");
System.out.println("listProp2: " + listProp2.getValue());
// Output:
listProp1: [one]
listProp2: [one, two]
1.5.4 绑定指定位置的元素
// Bind a fix position value. ObjectBinding<E> valueAt(int index) // Bind a dynamic position value. ObjectBinding<E> valueAt(ObservableIntegerValue index)
当给定的index,找不到元素时,ObjectBinding中的内容时null。
ListProperty<String> listProp1 = new SimpleListProperty<>(FXCollections.observableArrayList());
IntegerProperty elementIndex = new SimpleIntegerProperty(0);
ObjectBinding<String> element = listProp1.valueAt(elementIndex);
System.out.println(element.get());
listProp1.addAll(Lists.newArrayList("one", "two", "three"));
System.out.println(element.get());
elementIndex.set(2);
System.out.println(element.get());
// Output:
null
one
three
2. SetProperty
SetProperty的使用和上面说的ListProperty是一样的,唯一不同的是:SetProperty是无序的,因此没有valueAt()方法来绑定指定元素。
创建SetProperty:
- SimpleSetProperty()
- SimpleSetProperty(ObservableSet<E> initialValue)
- SimpleSetProperty(Object bean, String name)
- SimpleSetProperty(Object bean, String name, ObservableSet<E> initialValue)
3. MapProperty
MapProperty的使用和上面说的ListProperty是一样的,唯一不同的是:MapProperty使用valueAt()方法,通过传入一个key,来绑定指定的value。
创建MapProperty:
绑定指定key的value:
MapProperty<String, Integer> mapProp =
new SimpleMapProperty<>(FXCollections.observableHashMap());
mapProp.put("one", 1);
mapProp.put("two", 2);
mapProp.put("three", 3);
StringProperty mapKey = new SimpleStringProperty("one");
ObjectBinding<Integer> mapValue = mapProp.valueAt(mapKey);
System.out.println("key: " + mapKey.get() + ", value:" + mapValue.get());
mapKey.set("three");
System.out.println("key: " + mapKey.get() + ", value:" + mapValue.get());
// Output:
key: one, value:1
key: three, value:3
4. 总结
Collection Property (ListProperty, SetProperty和MapProperty)与其它普通Property,如IntegerProperty是一样的,只不过其存储的元素是一个集合,可以监听集合元素改变的事件。