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());
}
}
我遇到这个问题,当滑块旋钮处于最小或最大极限时,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());
}
}