8

I tested Spinner control in Java 8u40

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class MainApp extends Application
{
    public static void main(String[] args)
    {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage)
    {
        final Spinner spinner = new Spinner();

        spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 10000));
        spinner.setEditable(true);

        GridPane grid = new GridPane();
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(10));

        int row = 0;

        grid.add(new Label("Spinner:"), 0, row);
        grid.add(spinner, 1, row);

        Scene scene = new Scene(grid, 350, 300);

        stage.setTitle("Hello Spinner");
        stage.setScene(scene);
        stage.show();
    }
}

How I can only insert number into the spinner control field?

Now I can insert numbers and text. Is there any example that can be used as example?

kleopatra
  • 51,061
  • 28
  • 99
  • 211
user1285928
  • 1,328
  • 29
  • 98
  • 147

3 Answers3

12

Not entirely certain about the requirement - assuming that you want to prevent the input of characters that wouldn't parse to a valid Number.

If so, usage of a TextFormatter in the Spinner's editor comes to the rescue: with it, you'll monitor any change of text and either accept or reject it. The decision is encapsulated inside the formatter's filter. A very simple version (there's definitely more to do, see Swing's DefaultFormatter)

// get a localized format for parsing
NumberFormat format = NumberFormat.getIntegerInstance();
UnaryOperator<TextFormatter.Change> filter = c -> {
    if (c.isContentChange()) {
        ParsePosition parsePosition = new ParsePosition(0);
        // NumberFormat evaluates the beginning of the text
        format.parse(c.getControlNewText(), parsePosition);
        if (parsePosition.getIndex() == 0 ||
                parsePosition.getIndex() < c.getControlNewText().length()) {
            // reject parsing the complete text failed
            return null;
        }
    }
    return c;
};
TextFormatter<Integer> priceFormatter = new TextFormatter<Integer>(
        new IntegerStringConverter(), 0, filter);

spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(
        0, 10000, Integer.parseInt(INITAL_VALUE)));
spinner.setEditable(true);
spinner.getEditor().setTextFormatter(priceFormatter);
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • Not the orignial downvoter, but here is my explanation: your verbatim solution didn't work. When I tried to use it, the spinner's visible content didn't changed on spinning. Admittedly, I was in a hurry and didn't go in depth (never checked Swing's DefaultFormatter as you suggested), but the fact that your code broke the basic Spinner mechanic was a bummer - and in the end I resorted to TextFields with validation on submission, errors being shown on a dialog, which was an extremely sad, albeit somehow practical, solution. – Giulio Piancastelli Jan 28 '17 at 10:05
  • @GiulioPiancastelli thanks for your feedback :-) It's been a while but relatively certain that it did work at the time I wrote the answer, at least cant imagine that I wouldn't have checked the standard behaviour - shit happens, though. Might try one of these days and update if needed. – kleopatra Jan 29 '17 at 16:29
  • 1
    @GiulioPiancastelli hmm ... a quick check seems to work (in 8u102 and a very early preview of 9): clicking the up/down buttons spins as expected (spinning by keyboard doesn't work in any editable spinner) - care to explain what exactly you see not working? Thanks – kleopatra Jan 29 '17 at 17:03
4
  • because Spinner uses TextField as an editor, users can insert any String into spinner
  • only after user hits the return key then Spinners model (SpinnerValueFactory implementation) validates users input

so you have to add validation to Spinners editor by code:

// imports omitted

public class MainApp extends Application {

protected static final String INITAL_VALUE = "0";

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

@Override
public void start(Stage stage) {
    final Spinner spinner = new Spinner();

    spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 10000,
            Integer.parseInt(INITAL_VALUE)));
    spinner.setEditable(true);

    EventHandler<KeyEvent> enterKeyEventHandler;

    enterKeyEventHandler = new EventHandler<KeyEvent>() {

        @Override
        public void handle(KeyEvent event) {

            // handle users "enter key event"
            if (event.getCode() == KeyCode.ENTER) {

                try {
    // yes, using exception for control is a bad solution ;-)
            Integer.parseInt(spinner.getEditor().textProperty().get());
                }
                catch (NumberFormatException e) {

                    // show message to user: "only numbers allowed"

                    // reset editor to INITAL_VALUE
                    spinner.getEditor().textProperty().set(INITAL_VALUE);
                }
            }
        }
    };

    // note: use KeyEvent.KEY_PRESSED, because KeyEvent.KEY_TYPED is to late, spinners
    // SpinnerValueFactory reached new value before key released an SpinnerValueFactory will
    // throw an exception
    spinner.getEditor().addEventHandler(KeyEvent.KEY_PRESSED, enterKeyEventHandler);

    GridPane grid = new GridPane();
    grid.setHgap(10);
    grid.setVgap(10);
    grid.setPadding(new Insets(10));

    int row = 0;

    grid.add(new Label("Spinner:"), 0, row);
    grid.add(spinner, 1, row);

    Scene scene = new Scene(grid, 350, 300);

    stage.setTitle("Hello Spinner");
    stage.setScene(scene);
    stage.show();
}
}
  • try with not numbers: code will replace spinners editor content with INITIAL_VALUE
  • try with numbers out of border, spinners model will reset spinners editor to valid value
Marcus Bleil
  • 141
  • 1
  • 7
0

I know this is late answer, try this regex filter

public class IntegerSpinner<T> extends Spinner<T>{
    // All constructor call init method

    private void init(){
        getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
            if(!newValue.matches("\\d*")){
                getEditor().setText(oldValue);
            }
        });
    }
}
0009laH
  • 1,960
  • 13
  • 27
NM Naufaldo
  • 1,032
  • 1
  • 12
  • 30