如何在JavaFX中创建TimeSpinner?

我想让旋转器进入这样的时间: YoutubeVidéo

如果有人知道源代码隐藏在哪里,那将是完美的。 但如果没有,我想尝试自己实现它,但我怎么做这样的3个不同的(可聚焦?)textarea?

编辑:这是我得到的,但我希望能够选择小时和增量小时不仅是分钟(和秒的相同)

Spinner spinner = new Spinner(new SpinnerValueFactory() { { setConverter(new LocalTimeStringConverter(FormatStyle.MEDIUM)); } @Override public void decrement(int steps) { if (getValue() == null) setValue(LocalTime.now()); else { LocalTime time = (LocalTime) getValue(); setValue(time.minusMinutes(steps)); } } @Override public void increment(int steps) { if (this.getValue() == null) setValue(LocalTime.now()); else { LocalTime time = (LocalTime) getValue(); setValue(time.plusMinutes(steps)); } } }); spinner.setEditable(true); 

这是我得到的结果:

在此处输入图像描述

谢谢

我认为选择编辑器各个部分的最佳方法是检查编辑器中的caretPosition ,并根据需要增加/减少相应的部分。 您还可以在编辑器上设置TextFormatter来控制允许的输入等。

这是一个快速的尝试:不是为了生产质量,但应该是一个良好的开端:

 import java.time.LocalTime; import java.time.format.DateTimeFormatter; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.scene.control.Spinner; import javafx.scene.control.SpinnerValueFactory; import javafx.scene.control.TextFormatter; import javafx.scene.input.InputEvent; import javafx.util.StringConverter; public class TimeSpinner extends Spinner { // Mode represents the unit that is currently being edited. // For convenience expose methods for incrementing and decrementing that // unit, and for selecting the appropriate portion in a spinner's editor enum Mode { HOURS { @Override LocalTime increment(LocalTime time, int steps) { return time.plusHours(steps); } @Override void select(TimeSpinner spinner) { int index = spinner.getEditor().getText().indexOf(':'); spinner.getEditor().selectRange(0, index); } }, MINUTES { @Override LocalTime increment(LocalTime time, int steps) { return time.plusMinutes(steps); } @Override void select(TimeSpinner spinner) { int hrIndex = spinner.getEditor().getText().indexOf(':'); int minIndex = spinner.getEditor().getText().indexOf(':', hrIndex + 1); spinner.getEditor().selectRange(hrIndex+1, minIndex); } }, SECONDS { @Override LocalTime increment(LocalTime time, int steps) { return time.plusSeconds(steps); } @Override void select(TimeSpinner spinner) { int index = spinner.getEditor().getText().lastIndexOf(':'); spinner.getEditor().selectRange(index+1, spinner.getEditor().getText().length()); } }; abstract LocalTime increment(LocalTime time, int steps); abstract void select(TimeSpinner spinner); LocalTime decrement(LocalTime time, int steps) { return increment(time, -steps); } } // Property containing the current editing mode: private final ObjectProperty mode = new SimpleObjectProperty<>(Mode.HOURS) ; public ObjectProperty modeProperty() { return mode; } public final Mode getMode() { return modeProperty().get(); } public final void setMode(Mode mode) { modeProperty().set(mode); } public TimeSpinner(LocalTime time) { setEditable(true); // Create a StringConverter for converting between the text in the // editor and the actual value: DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss"); StringConverter localTimeConverter = new StringConverter() { @Override public String toString(LocalTime time) { return formatter.format(time); } @Override public LocalTime fromString(String string) { String[] tokens = string.split(":"); int hours = getIntField(tokens, 0); int minutes = getIntField(tokens, 1) ; int seconds = getIntField(tokens, 2); int totalSeconds = (hours * 60 + minutes) * 60 + seconds ; return LocalTime.of((totalSeconds / 3600) % 24, (totalSeconds / 60) % 60, seconds % 60); } private int getIntField(String[] tokens, int index) { if (tokens.length <= index || tokens[index].isEmpty()) { return 0 ; } return Integer.parseInt(tokens[index]); } }; // The textFormatter both manages the text <-> LocalTime conversion, // and vetoes any edits that are not valid. We just make sure we have // two colons and only digits in between: TextFormatter textFormatter = new TextFormatter(localTimeConverter, LocalTime.now(), c -> { String newText = c.getControlNewText(); if (newText.matches("[0-9]{0,2}:[0-9]{0,2}:[0-9]{0,2}")) { return c ; } return null ; }); // The spinner value factory defines increment and decrement by // delegating to the current editing mode: SpinnerValueFactory valueFactory = new SpinnerValueFactory() { { setConverter(localTimeConverter); setValue(time); } @Override public void decrement(int steps) { setValue(mode.get().decrement(getValue(), steps)); mode.get().select(TimeSpinner.this); } @Override public void increment(int steps) { setValue(mode.get().increment(getValue(), steps)); mode.get().select(TimeSpinner.this); } }; this.setValueFactory(valueFactory); this.getEditor().setTextFormatter(textFormatter); // Update the mode when the user interacts with the editor. // This is a bit of a hack, eg calling spinner.getEditor().positionCaret() // could result in incorrect state. Directly observing the caretPostion // didn't work well though; getting that to work properly might be // a better approach in the long run. this.getEditor().addEventHandler(InputEvent.ANY, e -> { int caretPos = this.getEditor().getCaretPosition(); int hrIndex = this.getEditor().getText().indexOf(':'); int minIndex = this.getEditor().getText().indexOf(':', hrIndex + 1); if (caretPos <= hrIndex) { mode.set( Mode.HOURS ); } else if (caretPos <= minIndex) { mode.set( Mode.MINUTES ); } else { mode.set( Mode.SECONDS ); } }); // When the mode changes, select the new portion: mode.addListener((obs, oldMode, newMode) -> newMode.select(this)); } public TimeSpinner() { this(LocalTime.now()); } } 

这是一个使用它的简单示例:

 import java.time.format.DateTimeFormatter; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class TimeSpinnerExample extends Application { @Override public void start(Stage primaryStage) { TimeSpinner spinner = new TimeSpinner(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm:ss a"); spinner.valueProperty().addListener((obs, oldTime, newTime) -> System.out.println(formatter.format(newTime))); StackPane root = new StackPane(spinner); Scene scene = new Scene(root, 350, 120); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } } 

将转换器构造函数更改为:

  setConverter(new LocalTimeStringConverter(DateTimeFormatter.ofPattern("HH:mm"), DateTimeFormatter.ofPattern("HH:mm"))); 

在此处输入图像描述