Cover by Patrick Niemeyer, Daniel Leuck

Safari, the world’s most comprehensive technology and business learning platform.

Find the exact information you need to solve a problem on the fly, or go deeper to master the technologies and skills you need to succeed

Start Free Trial

No credit card required

O'Reilly logo

The Spinner

JList and JComboBox are two ways to let the user choose from a set of values. A JComboBox has added flexibility when it is made editable, but, in general, both of these components are limited in that they can only prompt the user from a fixed set of choices. In Java 1.4, Swing added a component called JSpinner that is useful for large or open-ended sequences of values such as numbers or dates. The JSpinner is a cousin of the JComboBox; it displays a value in a field, but instead of providing a drop-down list of choices, it gives the user a small pair of up and down arrows for moving over a range of values (see Figure 17-5). Like the combo box, a JSpinner can also be made editable, allowing the user to type a valid value directly into the field.

Image of DateSelector application

Figure 17-5. Image of DateSelector application

Swing provides three basic types of Spinners, represented by three different data models for the JSpinner component: SpinnerListModel, SpinnerNumberModel, and SpinnerDateModel.

The SpinnerListModel acts like a combo box, specifying a fixed set of objects:

    String [] options = new String [] { "small", "medium", "large", "huge" };
    SpinnerListModel model = new SpinnerListModel( options );
    JSpinner spinner = new JSpinner( model );

You can retrieve the current value from the model at any time:

    String value = (String)model.getValue();

Alternatively, you can register a ChangeListener to receive updates as the user changes values. With a SpinnerListModel, if the spinner is editable and the user enters a value directly, it is validated against the set of choices before being accepted. This behavior is a little different from the other types of SpinnerModels which, when editable, accept any valid value of the correct type (e.g., a number or date).

The SpinnerNumberModel displays numeric values. It can be configured with initial, minimum, and maximum values:

    double initial=5.0, min=0.0, max=10.0, increment=0.1;
    SpinnerNumberModel model =
        new SpinnerNumberModel( initial, min, max, increment );
    JSpinner spinner = new JSpinner(model);

Here we have constructed a spinner with an initial value of 5.0 that allows the user to change the value to between 0 and 10.0 in increments of 0.1. The SpinnerNumberModel getNumber() method retrieves the current value.

Perhaps the most interesting feature of the JSpinner is the SpinnerDateModel, which allows the user to choose calendar dates by moving in specified increments of time. The SpinnerDateModel accepts a range, such as the SpinnerNumberModel, but the values are Date objects and the increment is a java.util.Calendar constant field such as Calendar.DAY, Calendar.WEEK, and so on. The following example, DateSelector, creates a JSpinner showing the current date and time. It allows the user to change the date in increments of one week, over a range of one year (six months forward or back). A ChangeListener is registered with the model to display the values as they are modified:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import java.util.*;

    public class DateSelector {
      public static void main(String[] args)
      {
        JFrame frame = new JFrame("DateSelector v1.0");

        Calendar now = Calendar.getInstance();
        Calendar earliest = (Calendar)now.clone();
        earliest.add( Calendar.MONTH, -6 );
        Calendar latest = (Calendar)now.clone();
        latest.add( Calendar.MONTH, 6 );
        SpinnerModel model = new SpinnerDateModel(
            now.getTime(), earliest.getTime(), latest.getTime(),
            Calendar.WEEK_OF_YEAR);
        final JSpinner spinner = new JSpinner(model);
        // Disable the built-in date editor
        spinner.setEditor(new JSpinner.DefaultEditor(spinner) );

        model.addChangeListener( new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                System.out.println( ((SpinnerDateModel)e.getSource())
                    .getDate() );
            }
        } );

        frame.getContentPane().add( "North", new JLabel("Choose a week") );
        frame.getContentPane().add( "Center", spinner );
        frame.pack();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setVisible(true);
      }
    }

As we said, the SpinnerCalendarModel acts just like the SpinnerNumberModel, except that it works with Date objects and uses the special Calendar constants as increments. To create dates, we construct a Calendar object for the correct time and use its getTime() method. In this example, we used the Calendar’s add() method to set the minimum and maximum values six months in each direction. Table 17-1 shows values for increments in the Calendar.

Table 17-1. Calendar field values

Field value

Increment

Calendar.MILLISECOND

One millisecond

Calendar.SECOND

One second

Calendar.MINUTE

One minute

Calendar.HOUR Calendar.HOUR_OF_DAY

One hour

Calendar.AM_PM

A.M. or P.M.

Calendar.DAY_OF_WEEK

Calendar.DAY_OF_MONTH

Calendar.DAY_OF_YEAR

One day

Calendar.MONTH

One month

Calendar.YEAR

One year

Calendar.ERA

B.C. or A.D. in the Gregorian calendar

The SpinnerDateModel uses the Calendar add() method with a value of 1 or -1 and the corresponding constant value to increment or decrement the value. Increments of one have the same effect on several of the constants, as indicated in Table 17-1.

Find the exact information you need to solve a problem on the fly, or go deeper to master the technologies and skills you need to succeed

Start Free Trial

No credit card required