连接具有 StringConverter 的 Slider 和 Spinner
对于数字输入,有时将模拟控件同步到文本显示会很方便.在这个 Swing
import java.text.NumberFormat;导入 javafx.application.Application;导入 javafx.beans.Observable;导入 javafx.geometry.Insets;导入javafx.scene.Scene;导入 javafx.scene.control.Slider;导入 javafx.scene.control.Spinner;导入 javafx.scene.layout.GridPane;导入javafx.stage.Stage;导入 javafx.util.converter.PercentageStringConverter;/*** @参见 https://stackoverflow.com/a/6067986/230513*/公共类 SpinSlider 扩展应用程序 {私有静态最终双 MIN = 0;私有静态最终双 MAX = 1;私人静态最终双INITIAL_VALUE = 0.5;私人静态最终双STEP_INCREMENT = 0.1;@覆盖公共无效开始(阶段主要阶段){primaryStage.setTitle("SpinSlider");滑块滑块 = 新滑块(MIN,MAX,INITIAL_VALUE);滑块.setBlockIncrement(STEP_INCREMENT);Spinner spinner = new Spinner(MIN, MAX, INITIAL_VALUE, STEP_INCREMENT);spinner.getValueFactory().setConverter(新的 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, 滑块, 微调器);root.setPadding(新插图(8, 8, 8, 8));root.setHgap(8);场景场景 = 新场景(根);primaryStage.setScene(场景);主舞台.show();}公共静态无效主要(字符串[]参数){启动(参数);}}
解决方案 您的 INITIAL_VALUE
首先用作微调器构造函数的 initialValue
参数.在内部,微调器的具体
import java.text.NumberFormat;导入 javafx.application.Application;导入 javafx.geometry.Insets;导入javafx.scene.Scene;导入 javafx.scene.control.Slider;导入 javafx.scene.control.Spinner;导入 javafx.scene.control.SpinnerValueFactory;导入 javafx.scene.layout.GridPane;导入javafx.stage.Stage;导入 javafx.util.converter.PercentageStringConverter;/*** @参见 https://stackoverflow.com/a/55427307/230513* @参见 https://stackoverflow.com/a/6067986/230513*/公共类 SpinSlider 扩展应用程序 {私有静态最终双 MIN = 0;私有静态最终双 MAX = 1;私人静态最终双INITIAL_VALUE = 0.5;私人静态最终双STEP_INCREMENT = 0.1;@覆盖公共无效开始(阶段主要阶段){primaryStage.setTitle("SpinSlider");滑块滑块 = 新滑块(MIN,MAX,INITIAL_VALUE);滑块.setBlockIncrement(STEP_INCREMENT);SpinnerValueFactory 工厂 = 新 SpinnerValueFactory.DoubleSpinnerValueFactory(MIN, MAX, INITIAL_VALUE, STEP_INCREMENT);factory.setConverter(new PercentageStringConverter(NumberFormat.getPercentInstance()));微调器微调器 = 新微调器(工厂);slider.valueProperty().bindBidirectional(spinner.getValueFactory().valueProperty());GridPane root = new GridPane();root.addRow(0, 滑块, 微调器);root.setPadding(新插图(8, 8, 8, 8));root.setHgap(8);场景场景 = 新场景(根);primaryStage.setScene(场景);主舞台.show();}公共静态无效主要(字符串[]参数){启动(参数);}}
For numeric input, it's sometimes convenient to synchronize an analog control to a text display. In this Swing example, a JSpinner
and a JSlider
each listen for change events, and each updates the other's model to match. A similar JavaFX program, shown below, connects a Spinner
and a Slider
, and these listeners keep the controls coordinated:
slider.valueProperty().addListener((Observable o) -> {
spinner.getValueFactory().setValue(slider.getValue());
});
spinner.valueProperty().addListener((Observable o) -> {
slider.setValue((double) spinner.getValue());
});
Unfortunately, when I added a StringConverter
to the spinner's SpinnerValueFactory
, the initial value was unformatted until either control was changed—even when setting the initial value explicitly again, after adding the converter:
spinner.getValueFactory().setConverter(…);
spinner.getValueFactory().setValue(INITIAL_VALUE);
Where am I going wrong?
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);
}
}
解决方案
Your INITIAL_VALUE
is first used as the initialValue
parameter to the spinner's constructor. Internally, the spinner's concrete SpinnerValueFactory
is a DoubleSpinnerValueFactory
that adds a ChangeListener
to its value
property; when the initial value is set again, the value hasn't really changed. Two approaches suggest themselves:
Specify a different value to the constructor and set the desired one after adding the converter:
Spinner spinner = new Spinner(MIN, MAX, -42, STEP_INCREMENT); spinner.getValueFactory().setConverter(…); spinner.getValueFactory().setValue(INITIAL_VALUE);
Construct a
SpinnerValueFactory
with the desired initial value and use it to construct the spinner:SpinnerValueFactory factory = new SpinnerValueFactory .DoubleSpinnerValueFactory(MIN, MAX, INITIAL_VALUE, STEP_INCREMENT); factory.setConverter(…); Spinner spinner = new Spinner(factory);
In addition, the example below replaces the two listeners with a bidirectional binding, which uses weak listeners to allow garbage collection of properties:
slider.valueProperty().bindBidirectional(
spinner.getValueFactory().valueProperty());
Trivially, the spinner and slider can control each other. More commonly, each can stay synchronized in the course of controlling a property held by another model:
model.xProperty().bindBidirectional(slider.valueProperty());
model.xProperty().bindBidirectional(spinner.getValueFactory().valueProperty());
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);
}
}
相关文章