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

时间:2018-06-28 19:10:24

标签: java javafx slider javafx-8

我有一个问题,即当滑块旋钮处于最小或最大极限时,JavaFX滑块不会为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位置上释放滑块的时候。这是预期的行为吗?还是我错过了什么?

2 个答案:

答案 0 :(得分:2)

问题是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());
    }

}

答案 1 :(得分:0)

好像已经发布了一个解决方案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

我发现的另一种解决方案是保留原来的ChangeListener,该ChangeListener仅检查isValueChanging-value,但将以下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);

这很丑,但是行得通。我不知道是否有一种更清洁的方法来实现所需的行为。