Подключение ползунка и прядильщика с StringConverter - PullRequest
5 голосов
/ 30 марта 2019

Для числового ввода иногда удобно синхронизировать аналоговый элемент управления с текстовым дисплеем. В этом примере Swing , JSpinner и JSlider каждый прослушивает события изменения, и каждый обновляет модель другого, чтобы соответствовать. Аналогичная программа JavaFX, показанная ниже, соединяет Spinner и Slider, и эти слушатели поддерживают согласованные элементы управления:

slider.valueProperty().addListener((Observable o) -> {
    spinner.getValueFactory().setValue(slider.getValue());
});
spinner.valueProperty().addListener((Observable o) -> {
    slider.setValue((double) spinner.getValue());
});

К сожалению, когда я добавил StringConverter к вращающемуся элементу SpinnerValueFactory, начальное значение не форматировалось до тех пор, пока не был изменен какой-либо элемент управления - даже при повторной установке начального значения, после после добавления преобразователя:

spinner.getValueFactory().setConverter(…);
spinner.getValueFactory().setValue(INITIAL_VALUE);

Куда я иду не так?

image

import java.text.NumberFormat;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.control.Spinner;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.converter.PercentageStringConverter;

/**
 * @see https://stackoverflow.com/a/6067986/230513
 */
public class SpinSlider extends Application {

    private static final double MIN = 0;
    private static final double MAX = 1;
    private static final double INITIAL_VALUE = 0.5;
    private static final double STEP_INCREMENT = 0.1;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("SpinSlider");
        Slider slider = new Slider(MIN, MAX, INITIAL_VALUE);
        slider.setBlockIncrement(STEP_INCREMENT);
        Spinner spinner = new Spinner(MIN, MAX, INITIAL_VALUE, STEP_INCREMENT);
        spinner.getValueFactory().setConverter(
            new PercentageStringConverter(NumberFormat.getPercentInstance()));
        spinner.getValueFactory().setValue(INITIAL_VALUE);
        slider.valueProperty().addListener((Observable o) -> {
            spinner.getValueFactory().setValue(slider.getValue());
        });
        spinner.valueProperty().addListener((Observable o) -> {
            slider.setValue((double) spinner.getValue());
        });
        GridPane root = new GridPane();
        root.addRow(0, slider, spinner);
        root.setPadding(new Insets(8, 8, 8, 8));
        root.setHgap(8);
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

1 Ответ

6 голосов
/ 30 марта 2019

Ваш INITIAL_VALUE впервые используется в качестве параметра initialValue для конструктора счетчика. Внутренне, бетон прядильщика SpinnerValueFactory является DoubleSpinnerValueFactory, который добавляет ChangeListener к его value свойству; когда начальное значение устанавливается снова, значение на самом деле не изменилось . Два подхода предлагают себя:

  1. Укажите другое значение для конструктора и установите желаемое после добавления конвертера:

    Spinner spinner = new Spinner(MIN, MAX, -42, STEP_INCREMENT);
    spinner.getValueFactory().setConverter(…);
    spinner.getValueFactory().setValue(INITIAL_VALUE);
    
  2. Создайте SpinnerValueFactory с желаемым начальным значением и используйте его для построения счетчика:

    SpinnerValueFactory factory = new SpinnerValueFactory
        .DoubleSpinnerValueFactory(MIN, MAX, INITIAL_VALUE, STEP_INCREMENT);
    factory.setConverter(…);
    Spinner spinner = new Spinner(factory);
    

Кроме того, приведенный ниже пример заменяет двух слушателей на двунаправленную привязку, которая использует слабые слушатели, чтобы разрешить сбор мусора для свойств:

slider.valueProperty().bindBidirectional(
    spinner.getValueFactory().valueProperty());

Тривиально, спиннер и слайдер могут управлять друг другом. Чаще всего каждый может оставаться синхронизированным в ходе управления свойством, принадлежащим другой модели:

model.xProperty().bindBidirectional(slider.valueProperty());
model.xProperty().bindBidirectional(spinner.getValueFactory().valueProperty());

image

import java.text.NumberFormat;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.converter.PercentageStringConverter;

/**
 * @see https://stackoverflow.com/a/55427307/230513
 * @see https://stackoverflow.com/a/6067986/230513
 */
public class SpinSlider extends Application {

    private static final double MIN = 0;
    private static final double MAX = 1;
    private static final double INITIAL_VALUE = 0.5;
    private static final double STEP_INCREMENT = 0.1;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("SpinSlider");
        Slider slider = new Slider(MIN, MAX, INITIAL_VALUE);
        slider.setBlockIncrement(STEP_INCREMENT);
        SpinnerValueFactory factory = new SpinnerValueFactory
            .DoubleSpinnerValueFactory(MIN, MAX, INITIAL_VALUE, STEP_INCREMENT);
        factory.setConverter(new PercentageStringConverter(
            NumberFormat.getPercentInstance()));
        Spinner spinner = new Spinner(factory);
        slider.valueProperty().bindBidirectional(spinner.getValueFactory().valueProperty());
        GridPane root = new GridPane();
        root.addRow(0, slider, spinner);
        root.setPadding(new Insets(8, 8, 8, 8));
        root.setHgap(8);
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}
...