How to handle correlated FX properties?

笑着哭i 提交于 2019-12-08 14:00:34

问题


Properties - ol' style bean properties just the same as blinky fx properties - work best if they are orthogonal to each other. They pose problems - again, both types - if they are somehow correlated. Which might happen in nasty Real World more often than we like.

As an example take the twosome selectedIndex/selectedItem: the index points to the position of the item in a list if it is contained, or is negative if not. Updating one requires to update the other as well.

For java beans, the solution is straightforward because the bean itself has full control about when to fire the changes. And for fx beans?

A base pattern used in SelectionModel

Property<T> theRealItemProperty = new SimpleObjectProperty<>();
ReadOnlyProperty<T> readOnlyItemProperty = new ReadOnlyObjectProperty(theRealItemProperty);
/**
 * Public access is read-only
 */
public final ReadOnlyProperty<T> itemProperty() {
    return readOnlyItemProperty;
}
/**
 *  Setting allowed for subclasses only (and mis-behaving package neighbors ;-)
 */
protected final void setItem(T item) {
    theRealItemProperty.set(item);
}
/**
 * Special setting api for general public
 */
public void select(T item) {
   setIndex(indexOf(item));
   setItem(item);
}

The properties fire at the time of calling their setters, the bean has no control. As internal state is unstable between those calls, listeners to the first must not access the second on receiving the notification .. as they can’t know which is the first to fire, they cannot rely on the bean being stable, ever.

What to do now, either on owning bean or client side? Somehow defer the fire? The ol' trick Platform.runLater() scattered all over application code? Stumped ..


回答1:


As suggested by James_D, ReactFX' InhiBeans come to the rescue.

When coding a bean from scratch, use the properties provided by that package - they extend core classes, have the same names so its as simple as exchanging the imports. Then guard modifying methods like:

public void select(T item) {
   Guard guard = Guard.multi(theRealItemProperty.guard(), 
       theRealIndexProperty.guard());
   setIndex(indexOf(item));
   setItem(item);
   guard.close();
}

When subclassing core classes that finalized their correlated properties an option might be to go dirty: replace them via reflection. An example is AbstractSelectionModelBase which currently is the base MultipleSelectionModel in my experiments

// going dirty: replace super's selectedItem/Index property 
// with guarded cousins 
itemReplacement = new ReadOnlyObjectWrapper<>(this, "selectedItem");
replaceField("selectedItem", itemReplacement);
indexReplacement = new ReadOnlyIntegerWrapper(this, "selectedIndex", -1);
replaceField("selectedIndex", indexReplacement);

// usage in methods that update single selection state
protected void syncSingleSelectionState(int selectedIndex) {
    Guard guard = Guard.multi(itemReplacement.guard(), indexReplacement.guard());
    setSelectedIndex(selectedIndex);
    if (selectedIndex > -1) {
        setSelectedItem(getModelItem(selectedIndex));
    } else {
        // PENDING JW: do better? can be uncontained item
        setSelectedItem(null);
    } 
    guard.close();
    focus(selectedIndex);
}

Would be cool to have (and use it!) such a feature in core.



来源:https://stackoverflow.com/questions/27186755/how-to-handle-correlated-fx-properties

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!