Слайдер JavaFX не вызывает ChangeListener valueProperty для минимальных и максимальных значений - PullRequest
0 голосов
/ 28 июня 2018

У меня есть эта проблема, когда ползунок JavaFX не вызывает ChangeListener для valueProperty, когда ползунок находится на минимальном или максимальном экстремуме. Я хочу выполнить некоторый код только после изменения значения ползунка (не во время перетаскивания ползунка). Я пытался добиться этого с помощью следующего кода:

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 голосов
/ 20 июля 2018

Эта проблема является следствием разработки свойств FX: в отличие от спецификации ol 'bean-компонентов, они полностью автономны, каждый срабатывает при изменении независимо от другого. Как следствие, слушатели одного свойства не должны запрашивать любое другое свойство при реагировании на изменение.

Применяется к контексту свойств ползунка value / isValueAdjusting:

  • при прослушивании значения, состояние настройки не указано
  • при прослушивании регулировки состояние значения не указано

В конце концов, API Слайдера просто слишком слаб, чтобы обслуживать общий вариант использования (реагировать на изменения, только если они завершены) без значительных усилий.

Выходом может быть пользовательский слайдер с улучшенным API. Схема ниже основана на наблюдении (будьте осторожны: детали реализации!), Что все внутренние устройства устанавливают конечное значение с помощью slider.adjustValue(value) Улучшенный слайдер

  • добавляет свойство Скорректированное значение, которое обновляется, когда изменение является окончательным
  • обновляет скорректированное значение в конце AdjustValue

План (неполный и не проверенный формально):

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

}
0 голосов
/ 28 июня 2018

Похоже, что уже опубликовано решение здесь . В основном все сводится к добавлению явных проверок для крайностей, чтобы 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, который только проверяет значение isValueChanging, но добавить следующий 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);

Это ужасно, но работает. Я не знаю, есть ли более чистый способ достижения желаемого поведения.

...