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是一样的,只不过其存储的元素是一个集合,可以监听集合元素改变的事件。