JavaFX Slider 不为最小值和最大值调用 valueProperty 的 ChangeListener

JavaFX Slider not invoking valueProperty's ChangeListener for min and max values

我遇到这个问题,当滑块旋钮处于最小或最大极限时,JavaFX Slider 不会为 valueProperty 调用 ChangeListener。我只想在更改滑块值后(而不是在拖动滑块时)执行一些代码。我尝试使用以下代码实现此目的:

Slider slider = new Slider();
slider.setMin(0);
slider.setMax(10);
slider.setMajorTickUnit(5);
slider.setMinorTickCount(5);
slider.setBlockIncrement(1);
slider.setSnapToTicks(true);
slider.setShowTickMarks(true);
slider.setShowTickLabels(true);

ChangeListener<? super Number> valueListener = (observable, oldValue, newValue) -> {
    if (!slider.isValueChanging()) {
        System.out.println("Value changed");
    }
};

slider.valueProperty().addListener(valueListener);

您会看到,当您拖动并释放滑块时,每次都会打印出字符串 Value changed,但在 0 或 10 位置释放滑块旋钮时除外。这是预期的行为吗?还是我遗漏了什么?

似乎已经发布了解决方案 here。它基本上归结为添加对极端情况的显式检查,以便 ChangeListener 看起来像这样:

ChangeListener<? super Number> valueListener = (observable, oldValue, newValue) -> {
    boolean isOnMin = newValue.doubleValue() == slider.getMin();
    boolean isOnMax = newValue.doubleValue() == slider.getMax();
    if (!slider.isValueChanging() || isOnMin || isOnMax) {
        System.out.println("Value changed");
    }
};

但是,此解决方案的缺点是它会立即调用 System.out.println 每当滑块接触任一边缘时,即使尚未释放鼠标按钮也是如此。

我找到的另一种解决方案是保留仅检查 isValueChanging-value 的原始 ChangeListener,但将以下 ChangeListener 添加到 valueChangingProperty:

 ChangeListener<? super Boolean> valueChangingListener = (observable, oldUpdating, newUpdating) -> {
    double value = slider.getValue();
    boolean notUpdatingAnymore = oldUpdating && !newUpdating;
    boolean isOnExtreme = value == slider.getMin() || value == slider.getMax();
    if (notUpdatingAnymore && isOnExtreme) {
        System.out.println("Value changed");
    }
};

slider.valueChangingProperty().addListener(valueChangingListener);

它很丑陋,但它确实有效。我不知道是否有更简洁的方法来实现所需的行为。

问题是 fx 属性设计的结果:与 ol' bean 规范相比,它们是完全独立的,每个都独立于另一个发生变化。结果是,一个 属性 的听众在对更改做出反应时不得查询任何其他 属性。

应用于 Slider 的 value/isValueAdjusting 属性的上下文:

  • 监听值时,调整状态未指定
  • 听调时,值状态未指定

归根结底,Slider 的 api 实在是太弱了,不费吹灰之力就无法满足常见用例(仅在更改完成后才对更改做出反应)。

一个出路可能是具有增强功能的自定义滑块 api。下面的大纲是基于观察(注意:实现细节!),所有内部都通过 slider.adjustValue(value) 设置最终值。增强滑块

  • 添加一个 属性 adjustedValue,它会在最终更改时更新
  • 更新 adjustValue 末尾的 adjustedValue

大纲(不完整且未经正式测试):

public static class AdjustedSlider extends Slider {

    private DoubleProperty adjustedValue;

    public AdjustedSlider() {
        super();
    }

    public AdjustedSlider(double min, double max, double value) {
        super(min, max, value);
    }

    public DoubleProperty adjustedValueProperty() {
        if (adjustedValue == null) {
            adjustedValue = new SimpleDoubleProperty(this, "adjustedValue", 0);
        }
        return adjustedValue;
    }

    public double getAdjustedValue() {
        return adjustedValueProperty().get();
    }

    private void setAdjustedValue(double value) {
        adjustedValueProperty().set(value);
    }

    @Override
    public void adjustValue(double newValue) {
        super.adjustValue(newValue);
        setAdjustedValue(getValue());
    }

}