How can I set 3 values in spinner

3.7k views Asked by At

I want time like 02:25:12AM hour,min,sec in one spinner. How can I do that? I have done only for one value like

<Spinner fx:id="spinner" layoutX="350.0" layoutY="10.0" initialValue="60"        
         max="120"  prefHeight="25.0" prefWidth="60.0" />

It shows error like

jaavaFx.scene.control.Spinner does not support min/max/intial propery

But it works fine. Now I want to do it for 3 values.

1

There are 1 answers

7
James_D On BEST ANSWER

As @UlukBiy says in the comments, you probably want to use three spinners here. You probably also want to implement "wrap around", so that if you increment the number of seconds past 59, it resets to 0 and the minutes increment, etc.

Also, to set the min and max, you set them on the SpinnerValueFactory, not on the Spinner itself. See the Javadocs for Spinner, SpinnerValueFactory, and SpinnerValueFactory.IntegerSpinnerValueFactory.

Here is a complete example (in FXML):

TimeSpinner.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory ?>

<HBox  xmlns:fx="http://javafx.com/fxml/1" fx:controller="TimeSpinnerController">
    <Spinner fx:id="hourSpinner" prefWidth="60">
        <valueFactory>
            <javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory fx:id="hourValueFactory">
                <min>0</min>
                <max>23</max>
                <wrapAround>true</wrapAround>
            </javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory>
        </valueFactory>
    </Spinner>
    <Spinner fx:id="minuteSpinner" prefWidth="60">
        <valueFactory>
            <javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory fx:id="minuteValueFactory">
                <min>0</min>
                <max>59</max>
                <wrapAround>true</wrapAround>
            </javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory>>
        </valueFactory>
    </Spinner>
    <Spinner fx:id="secondSpinner" prefWidth="60">
        <valueFactory>
            <javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory fx:id="secondValueFactory">
                <min>0</min>
                <max>59</max>
                <wrapAround>true</wrapAround>
            </javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory>>
        </valueFactory>
    </Spinner>
</HBox>

TimeSpinnerController:

import java.time.Duration;

import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.fxml.FXML;
import javafx.scene.control.Spinner;



public class TimeSpinnerController {

    @FXML
    private Spinner<Integer> hourSpinner ;
    @FXML
    private Spinner<Integer> minuteSpinner ;
    @FXML
    private Spinner<Integer> secondSpinner ;

    private ReadOnlyObjectWrapper<Duration> time = new ReadOnlyObjectWrapper<>();

    public void initialize() {
        minuteSpinner.valueProperty().addListener((obs, oldValue, newValue) -> {
            if (oldValue.intValue() == 59 && newValue.intValue() == 0) {
                hourSpinner.increment();
            }
            if (oldValue.intValue() == 0 && newValue.intValue() == 59) {
                hourSpinner.decrement();
            }
        });

        secondSpinner.valueProperty().addListener((obs, oldValue, newValue) -> {
            if (oldValue.intValue() == 59 && newValue.intValue() == 0) {
                minuteSpinner.increment();
            }
            if (oldValue.intValue() == 0 && newValue.intValue() == 59) {
                minuteSpinner.decrement();
            }
        });

        time.bind(Bindings.createObjectBinding(this::computeTime, hourSpinner.valueProperty(),
                minuteSpinner.valueProperty(), secondSpinner.valueProperty()));
    }

    public ReadOnlyObjectProperty<Duration> timeProperty() {
        return time.getReadOnlyProperty() ;
    }

    public Duration getTime() {
        return timeProperty().get();
    }

    private Duration computeTime() {
        int seconds = secondSpinner.getValue();
        int minutes = minuteSpinner.getValue();
        int hours = hourSpinner.getValue();
        int totalSeconds = (hours * 60 + minutes) * 60 + seconds ;
        return Duration.ofSeconds(totalSeconds);
    }
}

Test code:

import java.io.IOException;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TimeSpinnerTest extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("TimeSpinner.fxml"));
        HBox timeSpinner = loader.load();
        TimeSpinnerController timeController = loader.getController() ;
        Label label = new Label();

        label.textProperty().bind(Bindings.createStringBinding(() -> {
            long s = timeController.getTime().getSeconds() ;
            return String.format("%02d:%02d:%02d", s / 3600, (s / 60) % 60, s % 60);
        }, timeController.timeProperty()));

        VBox root = new VBox(10, timeSpinner, label);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(24));
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

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