Was ist ein JSpinner?
Die Komponente speichert eine Reihe von Auswahloptionen,
von denen jedoch, im Gegensatz zu der menubasierten JComboBox
, immer
nur eine sichtbar ist. Die Daten, die ein JSpinner
zur Auswahl anbietet, werden intern durch ein sog. Model
verwaltet und im GUI durch ein JFormattedTextField
präsentiert.
Rechts des angezeigten
Eintrags kann mit Hilfe zweier nach oben und unten
gerichteter Pfeile schrittweise durch die gespeicherten
Optionen navigiert werden. Sie müssen im
Standardfall natürlich textbasiert sein,
können, nach Austausch des Textfeldes durch setEditor()
gegen eine andere geeignete Komponente, jedoch auch
anderen Objekten entsprechen. Ein Beispiel hierzu findet
sich im Artikel JSpinner
mit Bildern.
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.
JSpinner ohne eigenes Model
public class SpinnerNumberBsp { public SpinnerNumberBsp() { init(); } 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 SpinnerNumberBsp()); } }
Das 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
lediglich 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.
Die Models
Der Inhalt eines JSpinner
wird durch ein
Model bereitgestellt und verwaltet. Hierzu bietet die
Java-Bibliothek die Klasse AbstractSpinnerModel
,
die das Interface SpinnerModel
implementiert und bei Bedarf überschrieben werden
kann. Für die häufigsten Anwendungen stehen
drei Convenience-Klassen zur Verfügung, die hier
kurz vorgestellt werden.
-
SpinnerNumberModel
-
Zur Auswahl numerischer Daten basierend auf den
verfügbaren primitiven Typen
Double, Float, Long, Integer, Short
oderByte
. -
SpinnerListModel
-
Ein Model, dem zur Initialisierung ein
Object
-Array oder eineList
übergeben werden. -
SpinnerDateModel
- Verwaltet Datum-Zeit-Objekte, durch die im Standardfall tageweise navigiert wird.
SpinnerNumberModel
Ein SpinnerNumberModel
kann verwendet
werden, wenn eine andere als die als Standard
implementierte ganzzahlige Integer
-Auswahlliste
oder eine abweichende Schrittweite benötigt werden.
In ihm können ein Startwert, ein Minimal- und
Maximalwert, sowie die Schrittweite definiert werden.
Die Werte sind in den Eigenschaften value,
minimum, maximum
und stepSize
gespeichert. Auf sie kann durch Accessor-Methoden
lesend und schreibend zugegriffen werden. Entsprechend
der Typ-Angabe oben können sowohl ganzzahlige, als
auch gebrochene Werte verwendet werden. Minimum und
Maximum können null
sein und erzeugen
dann eine offene, unbegrenzte Auswahlliste. Die vier
Werte werden dem Konstruktor des SpinnerNumberModel
als Argumente übergeben.
Das folgende Beispiel zeigt eine solche Anwendung, bei der der Auswahlbereich zwischen 2 und 8.5 liegt, 7.5 vorausgewählt ist und eine Schrittweite von 0.4 verwendet wird. Dies zeigt auch, dass der vorausgewählte Wert beliebig zwischen den Minimum- und Maximum-Grenzen liegen darf und die Schrittweite diesbezüglich nicht eingehalten werden muss.
public class SpinnerNumberModelBsp { public SpinnerNumberModelBsp() { init(); } public void init() { JFrame frame = new JFrame("Number-Model-Beispiel"); SpinnerNumberModel model = new SpinnerNumberModel(7.5, 2, 8.5, 0.4); JSpinner spinner = new JSpinner(model); 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 SpinnerNumberModelBsp()); } }
SpinnerListModel
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.
Das zweite Beispiel demonstriert dies nebst der
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 dessen Erzeugung als Parameter übergeben. Das
Model-Objekt wiederum dient als Argument 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. Er wird dann 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(); } }); } }
Ein rotierendes ListModel
Werden der erste oder letzte Spinnereintrag angezeigt,
so bleibt 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); } }
Hier werden die Indices der jeweils angezeigten Inhalte ermittelt und der letzte mit dem ersten, bzw. der erste mit dem letzten Eintrag verknüpft.
SpinnerDateModel
Ein SpinnerDateModel dient zur Auswahl von Datums- und Zeitobjekten. Als Standard werden Datum und Zeit der aktuellen Systemzeit in Form der gesetzten Gebietsvariablen verwendet.
Wird ein eigenes SpinnerDateModel
implementiert, so werden - ähnlich dem
SpinnerNumberModel - vier Parameter, ein Startwert, ein
Minimal- und Maximalwert, sowie die Schrittweite
übergeben. Die Werte sind hier jedoch in den
Eigenschaften value, start, end
und calendarField
gespeichert und können durch entsprechende Getter-
und Setter-Methoden manipuliert werden. Minimal- und
Maximalwerte können null
sein, um
keine Unter- und Obergrenze zu setzen. Die in calendarField
abgelegte Schrittweite muss den folgenden Konstanten der
Calendar
-Klasse entsprechen:
- Calendar.ERA
- Calendar.YEAR
- Calendar.MONTH
- Calendar.WEEK_OF_YEAR
- Calendar.WEEK_OF_MONTH
- Calendar.DAY_OF_MONTH
- Calendar.DAY_OF_YEAR
- Calendar.DAY_OF_WEEK
- Calendar.DAY_OF_WEEK_IN_MONTH
- Calendar.AM_PM
- Calendar.HOUR
- Calendar.HOUR_OF_DAY
- Calendar.MINUTE
- Calendar.SECOND
- Calendar.MILLISECOND
Das Verhalten eines mit SpinnerDateModel
belegten JSpinner weist einige Besonderheiten auf und
wird aus diesem Grund zusätzlich im Artikel JSpinner
zur Datumsauswahl behandelt.
Quellen
https://docs.oracle.com/javase/tutorial/uiswing/components/spinner.html
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.