在可观察的 SimpleMapProperty 中包装 EnumMap

Wrapping an EnumMap in a observable SimpleMapProperty

I need to add a SimpleMapProperty to a JavaFX service class, but I am not sure of the correct syntax of if I am using the correct approach. Note that I am not trying to make the JavaFX service appear like a Java Bean, I just need to know how to listen for updates to an EnumMap from a enum ModuleType (that can be TYPEA or TYPEB) and an associated Boolean flag. Essentially, this can be thought of as a pair of watchdog timers wrapped in a single EnumMap.

I am having trouble understanding how to add the underlying EnumMap entries (there should be 2 - one for each ModuleType described above).

public class UDPListenerService  extends Service<Void> {
    // 'watchdog' property
    private final MapProperty<ModuleType, Boolean> watchdog;

    // 'watchdog' SimpleMapProperty bound property getter
    public ObservableMap<ModuleType, Boolean> getWatchdog() {
      return watchdog.get();
    }
    // 'watchdog' SimpleMapProperty bound property setter
    public void setWatchdog(ObservableMap<ModuleType, Boolean> aValue) {
        watchdog.set(aValue);
    }
    // 'watchdog' SimpleMapProperty bound property
    public MapProperty<ModuleType, Boolean> watchdogProperty() {
        return watchdog;
    }

    /**
     * Constructor
     */
    public UDPListenerService()
    {
        this.watchdog = new SimpleMapProperty<>(
            FXCollections.observableHashMap());
    }

    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                updateMessage("Running...");
                while (!isCancelled()) {
                    try {
                        Thread.sleep(1000);
                        Platform.runLater(() -> {
                            try {
                                // update do some processing here
                                // . . .
                                // pet the watchdog
                                // setWatchdog
                                if (testforModuleType==ModuleType.TYPEA) {
                                    // please help with syntax
                                    setWatchdog(ModuleType.TYPEA, false);
                                } else {
                                    // please help with syntax
                                    setWatchdog(ModuleType.TYPEB, false);
                                }
                            } catch (StatusRuntimeException ex) {
                                // watchdog timed out - listener will
                                // update gui components
                                if (testforModuleType==ModuleType.TYPEA) {
                                    // please help with syntax
                                    setWatchdog(ModuleType.TYPEA, true);
                                } else {
                                    // please help with syntax
                                    setWatchdog(ModuleType.TYPEB, true);
                                }
                            }
                        });
                    } catch (InterruptedException ex) {}
                }
                updateMessage("Cancelled");
                return null;
            }
        };
    }
}

The way I use this class is in the JavaFX controller class where I add a listener that populates java gui elements depending on whether the associated Boolean flag is true or false.

通常只读映射 属性 用于此类行为,即只有 getter 的 ObservableMap 字段。只修改地图的内容;初始地图分配后,没有新地图分配给字段。

private final ObservableMap<ModuleType, Boolean> watchdog;

public ObservableMap<ModuleType, Boolean> getWatchdog() {
    return watchdog;
}

地图本身的修改方式与 java.util.Map 的修改方式相同,例如在这种情况下使用 put 方法。可以观察到变化,例如使用 MapChangeListenerBindings.valueAt.

此外,EnumMap 可用作 ObservableMap 的支持 Map,但要做到这一点,需要使用 observableMap 方法而不是 [=23] =]方法。

以下示例根据 ObservableMap.

中的值随机选择/取消选择 2 个复选框的值
private CheckBox checkBoxA;
private CheckBox checkBoxB;
private ObservableMap<ModuleType, Boolean> map;

@Override
public void start(Stage primaryStage) {
    checkBoxA = new CheckBox("type A");
    checkBoxB = new CheckBox("type B");

    map = FXCollections.observableMap(new EnumMap<>(ModuleType.class));

    initMapListeners();

    Thread t = new Thread(() -> {
        Random random = new Random();
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
            }
            boolean b1 = random.nextBoolean();
            boolean b2 = random.nextBoolean();
            Platform.runLater(() -> {
                map.put(ModuleType.TYPEA, b1);
                map.put(ModuleType.TYPEB, b2);
            });
        }
    });
    t.setDaemon(true);
    t.start();

    Scene scene = new Scene(new VBox(10, checkBoxA, checkBoxB));

    primaryStage.setScene(scene);
    primaryStage.show();
}

initMapListeners() 的以下两个实现都将根据地图值设置 CheckBox.selected 状态。

private void initMapListeners() {
    checkBoxA.selectedProperty().bind(Bindings.valueAt(map, ModuleType.TYPEA));
    checkBoxB.selectedProperty().bind(Bindings.valueAt(map, ModuleType.TYPEB));
}
private void initMapListeners() {
    map.addListener((MapChangeListener.Change<? extends ModuleType, ? extends Boolean> change) -> {
        if (change.wasAdded()) {
            if (change.getKey() == ModuleType.TYPEA) {
                checkBoxA.setSelected(change.getValueAdded());
            } else if (change.getKey() == ModuleType.TYPEB) {
                checkBoxB.setSelected(change.getValueAdded());
            }
        }
    });
}