![]() |
Start på kapittel om spesialiserte komponenter for grafiske brukergrensesnitt |
JSpinner
-objekterDersom de verdiene som skal være tillatt å bruke i et formatert tekstfelt har en opplagt rekkefølge, kan det være et alternativ å bruke en spinner. En spinner bruker et formatert tekstfelt. I tillegg har den to små knapper som kan brukes til syklisk gjennomløping av en liste av verdier. Spinneren kan altså brukes til å velge en verdi fra en serie av alternativer. På den måten har den tilsvarende funksjonalitet som en komboboks eller en listeboks. Følgende bilde viser et vindu med tre spinnere som har hver sin tilhørende ledetekst.
Vi skal seinere se på programkoden for programmet som dette hører til.
Som for andre swing-komponenter, så blir verdiene til en spinner lagret i en modell, ikke i selve spinnerobjektet. Som regel oppretter vi derfor en spinner ved at vi først oppretter modellen, og så bruker denne som konstruktørparameter når vi oppretter spinneren.
Som spinnermodell kan vi enten bruke en av klassebibliotekets forhåndsdefinerte modeller, eller vi kan definere vår egen. I klassebiblioteket er det definert tre spinnermodeller:
SpinnerListModel
List
SpinnerNumberModel
int
-verdier,
double
-verdier, eller som
Number
-objekter.
Vi kan spesifisere minimums- og maksimumsverdier, samt skrittlengde.SpinnerDateModel
Date
-objekter.
Vi kan spesifisere start- og sluttdatoer, samt hvilket felt (for eksempel
Calendar.YEAR
) som skal brukes som skrittlengde.Når vi setter modell for spinneren, så blir spinnerens editor automatisk
satt. I klassebiblioteket er det en editorklasse for hver av de tre
modellklassene listet opp ovenfor. Klassene heter henholdsvis
JSpinner.ListEditor
,
JSpinner.NumberEditor
og
JSpinner.DateEditor
.
De er alle subklasser til
JSpinner.DefaultEditor
og bruker et formatert tekstfelt ved
editeringen. Dersom vi bruker en modell som det ikke er definert editor for,
så vil editoren være av type JSpinner.DefaultEditor
. Denne
bruker et ikke-editerbart formatert tekstfelt i sin editor.
Vi kan endre formateringen som brukes av spinnerens standardeditor. Det kan vi gjøre enten ved selv å opprette og sette editor, eller ved å hente ut editorens formaterte tekstfelt og gjøre kall på metoder for dette.
Klassene JSpinner.NumberEditor
og
JSpinner.DateEditor
har konstruktører som tillater oss å
opprette en editor som formaterer sine data etter våre egne ønsker.
I det følgende programeksemplet blir for eksempel en dato formatert til å
vise bare måned og år på formatet 02/2012 ved at det for spinneren er skrevet
følgende kode:
datospinner.setEditor(new JSpinner.DateEditor(datospinner, "MM/yyyy"));
Dersom vi isteden ønsker å hente ut editorens formaterte tekstfelt og tilpasse dette, må vi være klar over at editoren i seg selv ikke er et formatert tekstfelt. Editoren er et panel som inneholder et formatert tekstfelt. Et eksempel på hvordan vi kan hente ut det formaterte tekstfeltet fra editoren og tilpasse dette, har vi i følgende kodeutdrag fra det følgende programeksemplet.
35 //Henter spinnerens formatterte tekstfelt. 36 JFormattedTextField ftf = getTextField(månedspinner); 37 if (ftf != null) 38 { 39 ftf.setColumns(8); //specify more width than we need 40 ftf.setHorizontalAlignment(JTextField.RIGHT); 41 } ... 94 /** 95 * Return the formatted text field used by the editor, or null 96 * if the editor doesn't descend from JSpinner.DefaultEditor. 97 */ 98 public JFormattedTextField getTextField(JSpinner spinner) 99 { 100 JComponent editor = spinner.getEditor(); 101 if (editor instanceof JSpinner.DefaultEditor) 102 { 103 return ((JSpinner.DefaultEditor) editor).getTextField(); 104 } 105 else 106 { 107 System.err.println("Unexpected editor type: " 108 + spinner.getEditor().getClass() 109 + " isn't a descendant of DefaultEditor"); 110 return null; 111 } 112 }
Fullstendig programkode for programmet
SpinnerDemo2
er gjengitt nedenfor. Programmet er en tilpasning av
SpinnerDemo.java
som finnes i The Java Tutorials.
Ovenfor er det vist bilde av programmets brukervindu.
Kjør programmet og legg merke til følgende:
1 import javax.swing.*; 2 import java.awt.*; 3 import java.util.Calendar; 4 import java.util.Date; 5 6 public class SpinnerDemo2 extends JPanel 7 { 8 public SpinnerDemo2() 9 { 10 super(new BorderLayout()); 11 String[] labels = 12 { 13 "Måned: ", "År: ", "En annen dato: " 14 }; 15 //Ledetekster til spinnerne: 16 JLabel måned = new JLabel(labels[ 0]); 17 JLabel år = new JLabel(labels[ 1]); 18 JLabel dato = new JLabel(labels[ 2]); 19 20 //Legger ut ledetekstene i et panel 21 JPanel labelpanel = new JPanel(new GridLayout(0, 1, 0, 5)); 22 labelpanel.add(måned); 23 labelpanel.add(år); 24 labelpanel.add(dato); 25 26 Calendar calendar = Calendar.getInstance(); 27 28 //Oppretter den første spinneren. 29 String[] monthStrings = getMonthStrings(); //får tak i månedsnavnene 30 31 //bruker standardmodell 32 SpinnerListModel monthModel = new SpinnerListModel(monthStrings); 33 JSpinner månedspinner = new JSpinner(monthModel); 34 35 //Henter spinnerens formatterte tekstfelt. 36 JFormattedTextField ftf = getTextField(månedspinner); 37 if (ftf != null) 38 { 39 ftf.setColumns(8); //specify more width than we need 40 ftf.setHorizontalAlignment(JTextField.RIGHT); 41 } 42 43 //Lager den andre spinneren. 44 int currentYear = calendar.get(Calendar.YEAR); 45 SpinnerModel yearModel = new SpinnerNumberModel( 46 currentYear, //startverdi 47 currentYear - 100, //min 48 currentYear + 100, //max 49 1); //skrittlengde 50 51 JSpinner årsspinner = new JSpinner(yearModel); 52 53 //Make the year be formatted without a thousands separator. 54 årsspinner.setEditor(new JSpinner.NumberEditor(årsspinner, "#")); 55 56 //Lager den tredje spinneren. 57 Date initDate = calendar.getTime(); 58 calendar.add(Calendar.YEAR, -100); 59 Date earliestDate = calendar.getTime(); 60 calendar.add(Calendar.YEAR, 200); 61 Date latestDate = calendar.getTime(); 62 SpinnerModel dateModel = new SpinnerDateModel(initDate, 63 earliestDate, 64 latestDate, 65 Calendar.YEAR); 66 //ignored for user input 67 JSpinner datospinner = new JSpinner(dateModel); 68 datospinner.setEditor(new JSpinner.DateEditor(datospinner, 69 "MM/yyyy")); 70 //Henter spinnerens formatterte tekstfelt. 71 ftf = getTextField(datospinner); 72 if (ftf != null) 73 { 74 ftf.setHorizontalAlignment(JTextField.RIGHT); 75 ftf.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 3)); 76 } 77 datospinner.setBorder(BorderFactory.createLineBorder( 78 Color.BLACK, 1)); 79 //XXX: No easy way to get to the buttons and change their border. 80 81 //Legger spinnerne ut i et eget panel. 82 JPanel spinnerpanel = new JPanel(new GridLayout(0, 1, 0, 5)); 83 spinnerpanel.add(månedspinner); 84 spinnerpanel.add(årsspinner); 85 spinnerpanel.add(datospinner); 86 87 //Legger labelpanelet til venstre og spinnerpanelet 88 //til høyre i dette panel. 89 setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); 90 add(labelpanel, BorderLayout.CENTER); 91 add(spinnerpanel, BorderLayout.LINE_END); 92 } 93 94 /** 95 * Return the formatted text field used by the editor, or null 96 * if the editor doesn't descend from JSpinner.DefaultEditor. 97 */ 98 public JFormattedTextField getTextField(JSpinner spinner) 99 { 100 JComponent editor = spinner.getEditor(); 101 if (editor instanceof JSpinner.DefaultEditor) 102 { 103 return ((JSpinner.DefaultEditor) editor).getTextField(); 104 } 105 else 106 { 107 System.err.println("Unexpected editor type: " 108 + spinner.getEditor().getClass() 109 + " isn't a descendant of DefaultEditor"); 110 return null; 111 } 112 } 113 114 /** 115 * DateFormatSymbols returns an extra, empty value at 116 * the end of the array of months. Remove it. 117 */ 118 static protected String[] getMonthStrings() 119 { 120 String[] months = new java.text.DateFormatSymbols().getMonths(); 121 int lastIndex = months.length - 1; 122 123 if (months[lastIndex] == null 124 || months[lastIndex].length() <= 0) 125 { //last item empty 126 String[] monthStrings = new String[lastIndex]; 127 System.arraycopy(months, 0, monthStrings, 0, lastIndex); 128 return monthStrings; 129 } 130 else 131 { //last item not empty 132 return months; 133 } 134 } 135 136 /** 137 * Create the GUI and show it. For thread safety, this method 138 * should be invoked from the event-dispatching thread. 139 */ 140 private static void createAndShowGUI() 141 { 142 //Create and set up the window. 143 JFrame frame = new JFrame("SpinnerDemo"); 144 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 145 146 //Create and set up the content pane. 147 JComponent newContentPane = new SpinnerDemo2(); 148 newContentPane.setOpaque(true); //content panes must be opaque 149 frame.setContentPane(newContentPane); 150 151 //Display the window. 152 frame.pack(); 153 frame.setVisible(true); 154 } 155 156 public static void main(String[] args) 157 { 158 //Schedule a job for the event-dispatching thread: 159 //creating and showing this application's GUI. 160 javax.swing.SwingUtilities.invokeLater(new Runnable() 161 { 162 public void run() 163 { 164 createAndShowGUI(); 165 } 166 }); 167 } 168 }
Hendelsene som blir generert nå vi endrer verdiene som vises av spinnere, er av type
ChangeEvent
.
De kan fanges opp av lytteobjekter av type
ChangeListener
.
Lytteobjekt av denne typen kan registreres med metoden addChangeListener
.
Det kan gjøres enten for spinneren eller for dens spinnermodell. Klassen
ChangeEvent
og interface ChangeListener
må importeres fra pakken
javax.swing.event
. Den eneste metoden som må implementeres for å definere
lytteobjekt er
public void stateChanged(ChangeEvent e)
{
...
}
Programmet
SpinnerDemo3
som er gjengitt
nedenfor er en videreføring av eksemplet ovenfor. Datospinneren (den som er nederst i
vinduet som programmet viser) er utstyrt med lytteobjekt av type
ChangeListener
. Dette virker på den måten at spinnerens
tilhørende formaterte tekstfelt får bakgrunnsfarge bestemt av hvilken måned spinnerens dato er
innstilt på. For hver av de fire årstidene vår, sommer, høst og vinter er det definert en
fargekonstant. Denne brukes som bakgrunnsfarge for nevnte tektsfelt, bestemt av hvilken
årstid spinnerens måned tilhører. Følgende bilde viser programvinduet når måneden er
innstilt på april. Vi ser at datospinnerens tekstfelt da har grønn bakgrunnsfarge.
Datospinnerens lytteobjekt er definert av den indre klassen Spinnerlytter
.
Den nye metoden setÅrstidsfarge
brukes av lytteobjektet til å få satt ønsket
bakgrunnsfarge. Metoden blir også kalt opp rett etter at datospinneren er opprettet og
initialisert, i konstruktøren for panelet som definerer contentpane for programvinduet.
1 import java.awt.BorderLayout; 2 import java.awt.Color; 3 import java.awt.GridLayout; 4 import java.util.Calendar; 5 import java.util.Date; 6 import javax.swing.*; 7 import javax.swing.event.*; 8 9 public class SpinnerDemo3 extends JPanel 10 { 11 private Calendar calendar; 12 private JSpinner datospinner; 13 public static final Color VÅRFARGE = new Color(0, 204, 51); 14 public static final Color SOMMERFARGE = Color.RED; 15 public static final Color HØSTFARGE = new Color(255, 153, 0); 16 public static final Color VINTERFARGE = Color.CYAN; 17 18 public SpinnerDemo3() 19 { 20 super(new BorderLayout()); 21 String[] labels = 22 { 23 "Måned: ", "År: ", "En annen dato: " 24 }; 25 //Ledetekster til spinnerne: 26 JLabel måned = new JLabel(labels[ 0]); 27 JLabel år = new JLabel(labels[ 1]); 28 JLabel dato = new JLabel(labels[ 2]); 29 30 //Legger ut ledetekstene i et panel 31 JPanel labelpanel = new JPanel(new GridLayout(0, 1, 0, 5)); 32 labelpanel.add(måned); 33 labelpanel.add(år); 34 labelpanel.add(dato); 35 36 calendar = Calendar.getInstance(); 37 38 //Lager den første spinneren. 39 String[] monthStrings = getMonthStrings(); //henter månedsnavn 40 41 //bruker standardmodell 42 SpinnerListModel monthModel = new SpinnerListModel(monthStrings); 43 JSpinner månedspinner = new JSpinner(monthModel); 44 45 //Henter spinnerens formatterte tekstfelt. 46 JFormattedTextField ftf = getTextField(månedspinner); 47 if (ftf != null) 48 { 49 ftf.setColumns(8); //specify more width than we need 50 ftf.setHorizontalAlignment(JTextField.RIGHT); 51 } 52 53 //Lager den andre spinneren. 54 int currentYear = calendar.get(Calendar.YEAR); 55 SpinnerModel yearModel = new SpinnerNumberModel( 56 currentYear, //startverdi 57 currentYear - 100, //min 58 currentYear + 100, //max 59 1); //skrittlengde 60 61 JSpinner årsspinner = new JSpinner(yearModel); 62 63 //Make the year be formatted without a thousands separator. 64 årsspinner.setEditor(new JSpinner.NumberEditor(årsspinner, "#")); 65 66 //Add the third spinner. 67 Date initDate = calendar.getTime(); 68 calendar.add(Calendar.YEAR, -100); 69 Date earliestDate = calendar.getTime(); 70 calendar.add(Calendar.YEAR, 200); 71 Date latestDate = calendar.getTime(); 72 SpinnerModel dateModel = new SpinnerDateModel(initDate, 73 earliestDate, 74 latestDate, 75 Calendar.YEAR); 76 //ignored for user input 77 datospinner = new JSpinner(dateModel); 78 datospinner.setEditor(new JSpinner.DateEditor(datospinner, 79 "MM/yyyy")); 80 //Henter spinnerens formatterte tekstfelt. 81 ftf = getTextField(datospinner); 82 if (ftf != null) 83 { 84 ftf.setHorizontalAlignment(JTextField.RIGHT); 85 ftf.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 3)); 86 } 87 datospinner.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); 88 //XXX: No easy way to get to the buttons and change their border. 89 datospinner.addChangeListener(new Spinnerlytter()); 90 setÅrstidsfarge(initDate); 91 92 //Legger spinnerne ut i et eget panel. 93 JPanel spinnerpanel = new JPanel(new GridLayout(0, 1, 0, 5)); 94 spinnerpanel.add(månedspinner); 95 spinnerpanel.add(årsspinner); 96 spinnerpanel.add(datospinner); 97 98 //Legger labelpanelet til venstre og spinnerpanelet til 99 //høyre i dette panel. 100 setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); 101 add(labelpanel, BorderLayout.CENTER); 102 add(spinnerpanel, BorderLayout.LINE_END); 103 } 104 105 /** 106 * Return the formatted text field used by the editor, or null 107 * if the editor doesn't descend from JSpinner.DefaultEditor. 108 */ 109 public JFormattedTextField getTextField(JSpinner spinner) 110 { 111 JComponent editor = spinner.getEditor(); 112 if (editor instanceof JSpinner.DefaultEditor) 113 { 114 return ((JSpinner.DefaultEditor) editor).getTextField(); 115 } 116 else 117 { 118 System.err.println("Unexpected editor type: " 119 + spinner.getEditor().getClass() 120 + " isn't a descendant of DefaultEditor"); 121 return null; 122 } 123 } 124 125 /** 126 * DateFormatSymbols returns an extra, empty value at 127 * the end of the array of months. Remove it. 128 */ 129 static protected String[] getMonthStrings() 130 { 131 String[] months = new java.text.DateFormatSymbols().getMonths(); 132 int lastIndex = months.length - 1; 133 134 if (months[lastIndex] == null 135 || months[lastIndex].length() <= 0) 136 { //last item empty 137 String[] monthStrings = new String[lastIndex]; 138 System.arraycopy(months, 0, monthStrings, 0, lastIndex); 139 return monthStrings; 140 } 141 else 142 { //last item not empty 143 return months; 144 } 145 } 146 147 /** 148 * Create the GUI and show it. For thread safety, this method 149 * should be invoked from the event-dispatching thread. 150 */ 151 private static void createAndShowGUI() 152 { 153 //Create and set up the window. 154 JFrame frame = new JFrame("SpinnerDemo"); 155 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 156 157 //Create and set up the content pane. 158 JComponent newContentPane = new SpinnerDemo3(); 159 newContentPane.setOpaque(true); //content panes must be opaque 160 frame.setContentPane(newContentPane); 161 162 //Display the window. 163 frame.pack(); 164 frame.setVisible(true); 165 } 166 167 protected void setÅrstidsfarge(Date dato) 168 { 169 calendar.setTime(dato); 170 int måned = calendar.get(Calendar.MONTH); 171 JFormattedTextField ftf = getTextField(datospinner); 172 if (ftf == null) 173 { 174 return; 175 } 176 switch (måned) 177 { 178 case 2: //mars 179 case 3: //april 180 case 4: //mai 181 ftf.setBackground(SpinnerDemo3.VÅRFARGE); 182 break; 183 case 5: //juni 184 case 6: //juli 185 case 7: //august 186 ftf.setBackground(SpinnerDemo3.SOMMERFARGE); 187 break; 188 case 8: //september 189 case 9: //oktober 190 case 10: //november 191 ftf.setBackground(SpinnerDemo3.HØSTFARGE); 192 break; 193 default: 194 ftf.setBackground(SpinnerDemo3.VINTERFARGE); 195 } 196 } 197 198 private class Spinnerlytter implements ChangeListener 199 { 200 public void stateChanged(ChangeEvent e) 201 { 202 SpinnerModel modell = datospinner.getModel(); 203 Date dato = ((SpinnerDateModel) modell).getDate(); 204 setÅrstidsfarge(dato); 205 } 206 } 207 208 public static void main(String[] args) 209 { 210 //Schedule a job for the event-dispatching thread: 211 //creating and showing this application's GUI. 212 javax.swing.SwingUtilities.invokeLater(new Runnable() 213 { 214 public void run() 215 { 216 createAndShowGUI(); 217 } 218 }); 219 } 220 }
Copyright © Kjetil Grønning, revidert av Eva Hadler Vihovde 2014
![]() |
Start på kapittel om spesialiserte komponenter for grafiske brukergrensesnitt |