Was ist ein JSpinner?

Ein JSpinner ist ein einzeiliges Auswahlmenu bevorzugt für sortierte Einträge, in dem durch Pfeilbuttons navigiert werden kann.

Ein JSpinner speichert eine Reihe von Auswahloptionen, von denen jedoch, im Gegensatz zu den Möglichkeiten der JComboBox, immer nur eine sichtbar ist. Rechts von den Einträgen kann durch zwei kleine nach oben und unten gerichtete Pfeile durch die angezeigten Optionen navigiert werden.
Die Daten, die ein JSpinner zur Auswahl anbietet, werden intern durch ein sog. Model verwaltet.

Die Instanzierung eines JSpinner kann entweder ohne oder mit Model erfolgen. Im ersten Fall wird ein Auswahlmenu gebildet, das intern ein vorgegebenes SpinnerNumberModel verwendet. Es stellt eine mit 0 vorausgewählte, in Einerschritten auf- und absteigend navigierbare, ganzzahlige Liste bereit.
Ein weiteres Beispiel demonstriert die Verwendung eines einfachen String-Models.

JSpinner ohne eigenes Model

public class SpinnerNumberBsp {

    public void init() {
        JFrame frame = new JFrame("JSpinner-Beispiel");
        JSpinner spinner = new JSpinner();
        spinner.addChangeListener(e -> System.out.println(spinner.getValue()));
        frame.add(spinner);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new SpinnerNumberBsp().init();
            }
        });
    }
}

Das erste Beispiel erzeugt ein auf JFrame basierendes Fenster mit einem JSpinner ohne Übergabe eines gesonderten Models. Wie erwähnt, wird in diesem Fall intern ein SpinnerNumberModel verwendet. Dem JSpinner-Objekt wurde legiglich ein ChangeListener hinzugefügt, der auf das "Blättern" in der Auswahlliste reagiert. Er gibt den im Menu gezeigten Wert auf der Konsole aus. Es muss hier beachtet werden, dass der nach Aktivieren des Navigationspfeils neu gewählte Eintrag durch getValue() zurückgegeben wird. Die Methoden getNextValue() und getPreviousValue() bieten hier weitere Auswahlmöglichkeiten.

JFrame-Fenster mit JSpinner

Die Models

Soll die Auswahl des JSpinner zwischen anderen als numerischen Objekten erfolgen, so kann ein SpinnerListModel übergeben werden, dessen Inhalt durch ein Array oder eine List definiert werden kann. Neben SpinnerNumberModel und SpinnerListModel stellen die Java-Bibliotheken noch eine dritte Klasse SpinnerDateModel bereit, die Datumsobjekte verwaltet und durch die im Standardfall tageweise navigiert werden kann. Alle drei Model-Klassen sind von AbstractSpinnerModel abgeleitet.

JSpinner mit SpinnerListModel

Das zweite Beispiel demonstriert die Verwendung eines SpinnerListModel und die Möglichkeit, dessen Daten zu wechseln. Zunächst werden zwei String-Arrays deklariert. Sie dienen als Datenquellen des Models. Eines der beiden Arrays wird dem Konstruktor des SpinnerListModel bei desser Erzeugung als Parameter übergeben. Das Model-Objekt wiederum dient als Parameter bei der Initialisierung des JSpinner.
Mittels eines BorderLayout werden JSpinner und ein JButton nebeneinander auf dem Fenster platziert. Ein ActionListener ruft bei Betätigung des Buttons die Methode changeList() auf. Sie dient dem Wechsel der Datenbasis des Models und übergibt diesem das jeweils andere Array. Der angezeigte Inhalt des JSpinner kann so gewechselt werden.
Innerhalb der Methode wird abgefragt welcher Inhalt durch das Model aktuell geladen ist und dieser gegen das jeweils andere Array getauscht. Hierbei muss beachtet werden, dass die Klasse keine direkte Methode zum Laden eines Arrays hat. Dies kann nur in Form einer List erfolgen, sodass das Array erst entsprechend umgewandelt werden muss.

public class SpinnerBsp {

    String[] deClassics = {  "Megola", "Standard", "D-Rad", "NSU", "Horex" };
    String[] itClassics = { "Moto Guzzi", "Gilera", "Moto Morini", "Aermacchi",
            "MV Agusta" };

    public SpinnerBsp() {
        init();
    }

    public void init() {
        JFrame frame = new JFrame();
        SpinnerListModel model = new SpinnerListModel(deClassics);
        JSpinner spinner = new JSpinner(model);
        spinner.setPreferredSize(new Dimension(120, 28));
        JPanel panel = new JPanel(new BorderLayout());
        JButton butt = new JButton("wechseln");
        butt.addActionListener(e -> changeList(frame, spinner));
        panel.add(spinner, BorderLayout.WEST);
        panel.add(butt, BorderLayout.EAST);
        frame.add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("JSpinner-Beispiel");
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void changeList(JFrame frame, JSpinner spinner) {
        SpinnerListModel mod = (SpinnerListModel) spinner.getModel();
        if (mod.getList().equals(Arrays.asList(deClassics))) {
            mod.setList(Arrays.asList(itClassics));
        } else {
            mod.setList(Arrays.asList(deClassics));
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new SpinnerBsp().init();
            }
        });
    }
}

JFrame-Fenster mit JSpinner und JButton

Ein rotierendes ListModel

Werden der erste oder letzte Spinnereintrag angezeigt, so bleiben ein Weiter- oder Zurückblättern in der Liste wirkungslos. Um eine rotierende Anzeige zu erzeugen, also vom letzten wieder zum ersten Eintrag und umgekehrt blättern zu können, müssen die Klasse SpinnerListModel erweitert und deren Methoden getNextValue() und getPreviousValue() überschrieben werden.

public class SpinnerClassicsModel extends SpinnerListModel {
    public SpinnerClassicsModel(Object[] o) {
        super(o);
    }

    @Override
    public Object getNextValue() {
        int index = this.getList().indexOf(this.getValue());
        return (index >= (this.getList().size() - 1)) ? this.getList().get(0)
                : this.getList().get(index + 1);
    }

    @Override
    public Object getPreviousValue() {
        int index = this.getList().indexOf(this.getValue());
        return (index <= 0) ? this.getList().get(this.getList().size() - 1)
                : this.getList().get(index - 1);
    }
}

Natürlich muss nun ein Objekt des neuen Models übergeben werden. Es verwendet dann wiederum das String-Array als Datenquelle.

//...
SpinnerClassicsModel model = new SpinnerClassicsModel(deClassics);
JSpinner spinner = new JSpinner(model);
//...
Quellen

https://docs.oracle.com/javase/tutorial/uiswing/components/spinner.html