The JFC Swing Tutorial: A Guide to Constructing GUIs (2nd Edition)
< Day Day Up > |
Release 1.4 introduced a new component called JSpinner . [137] Spinners are similar to combo boxes and lists in that they let the user choose from a range of values. Like editable combo boxes, spinners generally allow the user to type in a value. Unlike combo boxes, spinners don't have a drop-down list that can cover up other components . Because spinners don't display possible valuesonly the current value is visiblespinners are often used instead of combo boxes or lists when the set of possible values is extremely large. However, spinners should only be used when the possible values and their sequence are obvious. [137] JSpinner API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JSpinner.html. A spinner is a compound component with three subcomponents: two small buttons and an editor . The editor can be any JComponent , but by default is implemented as a panel that contains a formatted text field. The spinner's possible and current values are managed by its model . Figure 1 shows an application named SpinnerDemo that has three spinners. Figure 64. SpinnerDemo has three spinners used to specify dates.
The demo's Month spinner initially displays the name of the first month in the user's locale. The possible values for this spinner are specified using an array of strings. The Year spinner displays one of a range of integers, initialized to the current year. The Another Date spinner displays one in a range of dates (initially the current date) in a custom format that shows just the month and year. You might notice that the edges of the Another Date spinner look different from those of the spinners above it. This is because the example customizes the border of both that spinner and the formatted text field inside it. Try This:
To create a spinner, you generally create its model and then pass the model into the JSpinner constructor. For example: String[] monthStrings = getMonthStrings(); //get month names SpinnerListModel monthModel = new SpinnerListModel(monthStrings); JSpinner spinner = new JSpinner(monthModel); Using Standard Spinner Models and Editors
The Swing API provides three spinner models: SpinnerListModel [139]
[139] SpinnerListModel API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpinnerListModel.html. A spinner model whose values are defined by an array of objects or a List . SpinnerDemo 's Month spinner uses this model, initialized with an array derived from the value returned by the getMonths method of java.text.DateFormatSymbols . See SpinnerDemo.java for details. SpinnerNumberModel [140]
[140] SpinnerNumberModel API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpinnerNumberModel.html. Supports sequences of numbers, which can be expressed as double s, int s, or Number s. You can specify the minimum and maximum allowable values, as well as the step size the amount of each increment or decrement. The Year spinner uses this model, created with the following code: SpinnerModel model = new SpinnerNumberModel(currentYear, //initial value currentYear - 100, //min currentYear + 100, //max 1); //step SpinnerDateModel [141]
[141] SpinnerDateModel API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpinnerDateModel.html. Supports sequences of Date objects. You can specify the minimum and maximum dates, as well as the field (such as Calendar.YEAR ) to increment or decrement. Note, however, that some look and feels ignore the specified field and instead change the field that appears selected. The Another Date spinner uses this model, created with the following code: Date initDate = calendar.getTime(); calendar.add(Calendar.YEAR, -100); Date earliestDate = calendar.getTime(); calendar.add(Calendar.YEAR, 200); Date latestDate = calendar.getTime(); model = new SpinnerDateModel(initDate, earliestDate, latestDate, Calendar.YEAR); When you set the spinner's model, the spinner's editor is automatically set. The Swing API provides an editor class corresponding to each of the three model classes listed above. These classes JSpinner.ListEditor , [142] JSpinner.NumberEditor , [143] and JSpinner.DateEditor [144] are all subclasses of JSpinner.DefaultEditor [145] that feature editable formatted text fields. If you use a model that doesn't have an editor associated with it, the editor is by default a JSpinner.DefaultEditor instance with an uneditable formatted text field. [142] JSpinner.ListEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JSpinner.ListEditor.html. [143] JSpinner.NumberEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JSpinner.NumberEditor.html. [144] JSpinner.DateEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JSpinner.DateEditor.html. [145] JSpinner.DefaultEditor API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JSpinner.DefaultEditor.html. Specifying Spinner Formatting
To change the formatting used in a standard spinner editor, you can create and set the editor yourself. Another approach, which requires a little more code but gives you more options when using a default editor, is to get the editor's formatted text field and invoke methods on it. The JSpinner.NumberEditor and JSpinner.DateEditor classes have constructors that allow you to create an editor that formats its data a particular way. For example, the following code sets up the Another Date spinner so that instead of using the default date format, which is long and includes the time, it shows just the month and year in a compact way. spinner.setEditor(new JSpinner.DateEditor(spinner, "MM/yyyy"));
Note: You can play with date formats by running ComboBoxDemo2 using Java Web Start. For information about format strings, see the "Formatting" lesson of the Internationalization trail: JavaTutorial/i18n/format/index.html . Tables of number format characters are in the section "Number Format Pattern Syntax."
If you wish to invoke methods directly on the spinner's formatted text fieldto set its horizontal alignment, for exampleyou can get it using the getTextField method defined in JSpinner.DefaultEditor . Note that the Swing-provided editors aren't themselves formatted text fields. Instead, they're JPanel s that contain a formatted text field. Here's an example of getting and invoking methods on the editor's formatted text field: //Tweak the spinner's formatted text field. ftf = getTextField(spinner); if (ftf != null ) { ftf.setColumns(8); //specify more width than we need ftf.setHorizontalAlignment(JTextField.RIGHT); } ... public JFormattedTextField getTextField(JSpinner spinner) { JComponent editor = spinner.getEditor(); if (editor instanceof JSpinner.DefaultEditor) { return ((JSpinner.DefaultEditor)editor).getTextField(); } else { System.err.println("Unexpected editor type: " + spinner.getEditor().getClass() + " isn't a descendant of DefaultEditor"); return null; } } Creating Custom Spinner Models and Editors
If the existing spinner models or editors don't meet your needs, you can create your own. The easiest route to creating a custom spinner model is to create a subclass of an existing AbstractSpinnerModel [146] subclass that already does most of what you need. An alternative is to implement your own class by extending AbstractSpinnerModel , which implements the event notifications required for all spinner models. [146] AbstractSpinnerModel API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/AbstractSpinnerModel.html. The following subclass of SpinnerListModel implements a spinner model that cycles through an array of objects. It also lets you specify a second spinner's model to be updated whenever the cycle begins again. For example, if the array of objects is a list of months, the linked model could be for a spinner that displays the year. When the month flips over from December to January the year is incremented. Similarly, when the month flips back from January to December the year is decremented. public class CyclingSpinnerListModel extends SpinnerListModel { Object firstValue, lastValue; SpinnerModel linkedModel = null; public CyclingSpinnerListModel(Object[] values) { super(values); firstValue = values[0]; lastValue = values[values.length - 1]; } public void setLinkedModel(SpinnerModel linkedModel) { this.linkedModel = linkedModel; } public Object getNextValue() { Object value = super.getNextValue(); if (value == null) { value = firstValue; if (linkedModel != null) { linkedModel.setValue(linkedModel.getNextValue()); } } return value; } public Object getPreviousValue() { Object value = super.getPreviousValue(); if (value == null) { value = lastValue; if (linkedModel != null) { linkedModel.setValue(linkedModel.getPreviousValue()); } } return value; } }
As we mentioned before, if you implement a spinner model that does not descend from SpinnerListModel , SpinnerNumberModel , or SpinnerDateModel , then the spinner's default editor is an uneditable instance of JSpinner.DefaultEditor . As you've already seen, you can set the editor of a spinner by invoking the setEditor method on the spinner after the spinner's model property has been set. An alternative to using setEditor is to create a subclass of JSpinner and override its createEditor method so that it returns a particular kind of editor whenever the spinner model is of a certain type. In theory at least, you can use any JComponent as an editor. Possibilities include using a subclass of a standard component such as JLabel , or a component you've implemented from scratch, or a subclass of JSpinner.DefaultEditor . The only requirements are that the editor must be updated to reflect changes in the spinner's value, and it must have a reasonable preferred size. The editor should generally also set its tool tip text to whatever tool tip text has been specified for the spinner. An example of implementing an editor is in the next section. Detecting Spinner Value Changes
You can detect that a spinner's value has changed by registering a change listener on either the spinner or its model. Here's an example of implementing such a change listener. It's from SpinnerDemo3 , which is based on SpinnerDemo and uses a change listener to change the color of some text to match the value of the Another Date spinner.
Here's the implementation of SpinnerDemo3 : public class SpinnerDemo3 extends JPanel implements ChangeListener { protected Calendar calendar; protected JSpinner dateSpinner; ... public SpinnerDemo3() { ... SpinnerDateModel dateModel = ...; ... setSeasonalColor(dateModel.getDate()); //initialize color //Listen for changes on the date spinner. dateSpinner.addChangeListener(this); ... } public void stateChanged(ChangeEvent e) { SpinnerModel dateModel = dateSpinner.getModel(); if (dateModel instanceof SpinnerDateModel) { setSeasonalColor(((SpinnerDateModel)dateModel).getDate()); } } protected void setSeasonalColor(Date date) { calendar.setTime(date); int month = calendar.get(Calendar.MONTH); JFormattedTextField ftf = getTextField(dateSpinner); if (ftf == null) return; //Set the color to match northern hemisphere seasonal conventions. switch (month) { case 2: //March case 3: //April case 4: //May ftf.setForeground(SPRING_COLOR); break; ... default: //December, January, February ftf.setForeground(WINTER_COLOR); } } ... }
...//Where the components are created: JSpinner spinner = new JSpinner(new GrayModel(170)); spinner.setEditor(new GrayEditor(spinner)); class GrayModel extends SpinnerNumberModel { ... } class GrayEditor extends JLabel implements ChangeListener { public GrayEditor(JSpinner spinner) { setOpaque(true); ... //Get info from the model. GrayModel myModel = (GrayModel)(spinner.getModel()); setBackground(myModel.getColor()); spinner.addChangeListener(this); ... updateToolTipText(spinner); } protected void updateToolTipText(JSpinner spinner) { String toolTipText = spinner.getToolTipText(); if (toolTipText != null) { //JSpinner has tool tip text. Use it. if (!toolTipText.equals(getToolTipText())) { setToolTipText(toolTipText); } } else { //Define our own tool tip text. GrayModel myModel = (GrayModel)(spinner.getModel()); int rgb = myModel.getIntValue(); setToolTipText("(" + rgb + "," + rgb + "," + rgb + ")"); } } public void stateChanged(ChangeEvent e) { JSpinner mySpinner = (JSpinner)(e.getSource()); GrayModel myModel = (GrayModel)(mySpinner.getModel()); setBackground(myModel.getColor()); updateToolTipText(mySpinner); } } The Spinner API
Tables 76 through 81 list some of the commonly used API for spinners. If you need to deal directly with the editor's formatted text field, you should also see The Formatted Text Field API (page 231). Other methods you might use are listed in the API tables in The JComponent Class (page 53). Also see the JSpinner API documentation at: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JSpinner.html. Table 76. Classes Related to Spinners
Table 77. Useful JSpinner Constructors and Methods
Table 78. Useful Editor Constructors and Methods
Table 79. SpinnerListModel Methods
Table 80. SpinnerDateModel Methods
Table 81. SpinnerNumberModel Methods
Examples That Use Spinners
The following examples use spinners.
|
< Day Day Up > |