Choice Components

   


You now know how to collect text input from users, but there are many occasions for which you would rather give users a finite set of choices than have them enter the data in a text component. Using a set of buttons or a list of items tells your users what choices they have. (It also saves you the trouble of error checking.) In this section, you learn how to program checkboxes, radio buttons, lists of choices, and sliders.

Checkboxes

If you want to collect just a "yes" or "no" input, use a checkbox component. Checkboxes automatically come with labels that identify them. The user usually checks the box by clicking inside it and turns off the check mark by clicking inside the box again. To toggle the check mark, the user can also press the space bar when the focus is in the checkbox.

Figure 9-15 shows a simple program with two checkboxes, one to turn on or off the italic attribute of a font, and the other for boldface. Note that the second checkbox has focus, as indicated by the rectangle around the label. Each time the user clicks one of the checkboxes, we refresh the screen, using the new font attributes.

Figure 9-15. Checkboxes


Checkboxes need a label next to them to identify their purpose. You give the label text in the constructor.

 bold = new JCheckBox("Bold"); 

You use the setSelected method to turn a checkbox on or off. For example,

 bold.setSelected(true); 

The isSelected method then retrieves the current state of each checkbox. It is false if unchecked; true if checked.

When the user clicks on a checkbox, this triggers an action event. As always, you attach an action listener to the checkbox. In our program, the two checkboxes share the same action listener.

 ActionListener listener = . . . bold.addActionListener(listener); italic.addActionListener(listener); 

The actionPerformed method queries the state of the bold and italic checkboxes and sets the font of the panel to plain, bold, italic, or both bold and italic.

 public void actionPerformed(ActionEvent event) {    int mode = 0;    if (bold.isSelected()) mode += Font.BOLD;    if (italic.isSelected()) mode += Font.ITALIC;    label.setFont(new Font("Serif", mode, FONTSIZE)); } 

Example 9-5 is the complete program listing for the checkbox example.

Example 9-5. CheckBoxTest.java
  1. import java.awt.*;  2. import java.awt.event.*;  3. import javax.swing.*;  4.  5. public class CheckBoxTest  6. {  7.    public static void main(String[] args)  8.    {  9.       CheckBoxFrame frame = new CheckBoxFrame(); 10.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11.       frame.setVisible(true); 12.    } 13. } 14. 15. /** 16.    A frame with a sample text label and checkboxes for 17.    selecting font attributes. 18. */ 19. class CheckBoxFrame extends JFrame 20. { 21.    public CheckBoxFrame() 22.    { 23.       setTitle("CheckBoxTest"); 24.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 25. 26.       // add the sample text label 27. 28.       label = new JLabel("The quick brown fox jumps over the lazy dog."); 29.       label.setFont(new Font("Serif", Font.PLAIN, FONTSIZE)); 30.       add(label, BorderLayout.CENTER); 31. 32.       // this listener sets the font attribute of 33.       // the label to the checkbox state 34. 35.       ActionListener listener = new 36.          ActionListener() 37.          { 38.             public void actionPerformed(ActionEvent event) 39.             { 40.                int mode = 0; 41.                if (bold.isSelected()) mode += Font.BOLD; 42.                if (italic.isSelected()) mode += Font.ITALIC; 43.                label.setFont(new Font("Serif", mode, FONTSIZE)); 44.             } 45.          }; 46. 47.       // add the checkboxes 48. 49.       JPanel buttonPanel = new JPanel(); 50. 51.       bold = new JCheckBox("Bold"); 52.       bold.addActionListener(listener); 53.       buttonPanel.add(bold); 54. 55.       italic = new JCheckBox("Italic"); 56.       italic.addActionListener(listener); 57.       buttonPanel.add(italic); 58. 59.       add(buttonPanel, BorderLayout.SOUTH); 60.    } 61. 62.    public static final int DEFAULT_WIDTH = 300; 63.    public static final int DEFAULT_HEIGHT = 200; 64. 65.    private JLabel label; 66.    private JCheckBox bold; 67.    private JCheckBox italic; 68. 69.    private static final int FONTSIZE = 12; 70. } 


 javax.swing.JCheckBox 1.2 

  • JCheckBox(String label)

    constructs a checkbox with the given label that is initially unselected.

  • JCheckBox(String label, boolean state)

    constructs a checkbox with the given label and initial state.

  • JCheckBox(String label, Icon icon)

    constructs a checkbox with the given label and icon that is initially unselected.

  • boolean isSelected ()

    returns the state of the checkbox.

  • void setSelected(boolean state)

    sets the checkbox to a new state.

Radio Buttons

In the previous example, the user could check either, both, or neither of the two checkboxes. In many cases, we want to require the user to check only one of several boxes. When another box is checked, the previous box is automatically unchecked. Such a group of boxes is often called a radio button group because the buttons work like the station selector buttons on a radio. When you push in one button, the previously depressed button pops out. Figure 9-16 shows a typical example. We allow the user to select a font size from among the choices Small, Medium, Large, and Extra large but, of course, we will allow the user to select only one size at a time.

Figure 9-16. A radio button group


Implementing radio button groups is easy in Swing. You construct one object of type ButtonGroup for every group of buttons. Then, you add objects of type JRadioButton to the button group. The button group object is responsible for turning off the previously set button when a new button is clicked.

 ButtonGroup group = new ButtonGroup(); JRadioButton smallButton = new JRadioButton("Small", false); group.add(smallButton); JRadioButton mediumButton = new JRadioButton("Medium", true); group.add(mediumButton);] . . . 

The second argument of the constructor is true for the button that should be checked initially and false for all others. Note that the button group controls only the behavior of the buttons; if you want to group the buttons for layout purposes, you also need to add them to a container such as a JPanel.

If you look again at Figures 9-15 and 9-16, you will note that the appearance of the radio buttons is different from that of checkboxes. Checkboxes are square and contain a check mark when selected. Radio buttons are round and contain a dot when selected.

The event notification mechanism for radio buttons is the same as for any other buttons. When the user checks a radio button, the radio button generates an action event. In our example program, we define an action listener that sets the font size to a particular value:

 ActionListener listener = new    ActionListener()    {       public void actionPerformed(ActionEvent event)       {          // size refers to the final parameter of the addRadioButton method          label.setFont(new Font("Serif", Font.PLAIN, size));       }    }; 

Compare this listener setup with that of the checkbox example. Each radio button gets a different listener object. Each listener object knows exactly what it needs to do set the font size to a particular value. In the case of the checkboxes, we used a different approach. Both checkboxes have the same action listener. It called a method that looked at the current state of both checkboxes.

Could we follow the same approach here? We could have a single listener that computes the size as follows:

 if (smallButton.isSelected()) size = 8; else if (mediumButton.isSelected()) size = 12; . . . 

However, we prefer to use separate action listener objects because they tie the size values more closely to the buttons.

NOTE

If you have a group of radio buttons, you know that only one of them is selected. It would be nice to be able to quickly find out which one without having to query all the buttons in the group. Because the ButtonGroup object controls all buttons, it would be convenient if this object could give us a reference to the selected button. Indeed, the ButtonGroup class has a getSelection method, but that method doesn't return the radio button that is selected. Instead, it returns a ButtonModel reference to the model attached to the button. Unfortunately, none of the ButtonModel methods are very helpful. The ButtonModel interface inherits a method getSelectedObjects from the ItemSelectable interface that, rather uselessly, returns null. The getActionCommand method looks promising because the "action command" of a radio button is its text label. But the action command of its model is null. Only if you explicitly set the action commands of all radio buttons with the setActionCommand method do the models' action command values also get set. Then you can retrieve the action command of the currently selected button with buttonGroup.getSelection().getActionCommand().


Example 9-6 is the complete program for font size selection that puts a set of radio buttons to work.

Example 9-6. RadioButtonTest.java
  1. import java.awt.*;  2. import java.awt.event.*;  3. import javax.swing.*;  4.  5. public class RadioButtonTest  6. {  7.    public static void main(String[] args)  8.    {  9.       RadioButtonFrame frame = new RadioButtonFrame(); 10.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11.       frame.setVisible(true); 12.    } 13. } 14. 15. /** 16.    A frame with a sample text label and radio buttons for 17.    selecting font sizes. 18. */ 19. class RadioButtonFrame extends JFrame 20. { 21.    public RadioButtonFrame() 22.    { 23.       setTitle("RadioButtonTest"); 24.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 25. 26.       // add the sample text label 27. 28.       label = new JLabel("The quick brown fox jumps over the lazy dog."); 29.       label.setFont(new Font("Serif", Font.PLAIN, DEFAULT_SIZE)); 30.       add(label, BorderLayout.CENTER); 31. 32.       // add the radio buttons 33. 34.       buttonPanel = new JPanel(); 35.       group = new ButtonGroup(); 36. 37.       addRadioButton("Small", 8); 38.       addRadioButton("Medium", 12); 39.       addRadioButton("Large", 18); 40.       addRadioButton("Extra large", 36); 41. 42.       add(buttonPanel, BorderLayout.SOUTH); 43.    } 44. 45.    /** 46.       Adds a radio button that sets the font size of the 47.       sample text. 48.       @param name the string to appear on the button 49.       @param size the font size that this button sets 50.    */ 51.    public void addRadioButton(String name, final int size) 52.    { 53.       boolean selected = size == DEFAULT_SIZE; 54.       JRadioButton button = new JRadioButton(name, selected); 55.       group.add(button); 56.       buttonPanel.add(button); 57. 58.       // this listener sets the label font size 59. 60.       ActionListener listener = new 61.          ActionListener() 62.          { 63.             public void actionPerformed(ActionEvent event) 64.             { 65.                // size refers to the final parameter of the addRadioButton method 66.                label.setFont(new Font("Serif", Font.PLAIN, size)); 67.             } 68.          }; 69. 70.       button.addActionListener(listener); 71.    } 72. 73.    public static final int DEFAULT_WIDTH = 400; 74.    public static final int DEFAULT_HEIGHT = 200; 75. 76.    private JPanel buttonPanel; 77.    private ButtonGroup group; 78.    private JLabel label; 79. 80.    private static final int DEFAULT_SIZE = 12; 81. } 


 javax.swing.JRadioButton 1.2 

  • JRadioButton(String label, boolean state)

    constructs a radio button with the given label and initial state.

  • JRadioButton(String label, Icon icon)

    constructs a radio button with the given label and icon that is initially unselected.


 javax.swing.ButtonGroup 1.2 

  • void add(AbstractButton b)

    adds the button to the group.

  • ButtonModel getSelection()

    returns the button model of the selected button.


 javax.swing.ButtonModel 1.2 

  • String getActionCommand()

    returns the action command for this button model.


 javax.swing.AbstractButton 1.2 

  • void setActionCommand(String s)

    sets the action command for this button and its model.

Borders

If you have multiple groups of radio buttons in a window, you will want to visually indicate which buttons are grouped. Swing provides a set of useful borders for this purpose. You can apply a border to any component that extends JComponent. The most common usage is to place a border around a panel and fill that panel with other user interface elements such as radio buttons.

You can choose from quite a few borders, but you follow the same steps for all of them.

1.

Call a static method of the BorderFactory to create a border. You can choose among the following styles (see Figure 9-17):

  • Lowered bevel

  • Raised bevel

  • Etched

  • Line

  • Matte

  • Empty (just to create some blank space around the component)

Figure 9-17. Testing border types


2.

If you like, add a title to your border by passing your border to BorderFactory.createTitledBorder.

3.

If you really want to go all out, combine several borders with a call to BorderFactory.createCompoundBorder.

4.

Add the resulting border to your component by calling the setBorder method of the JComponent class.

For example, here is how you add an etched border with a title to a panel:

 Border etched = BorderFactory.createEtchedBorder() Border titled = BorderFactory.createTitledBorder(etched, "A Title"); panel.setBorder(titled); 

Run the program in Example 9-7 to get an idea what the various borders look like.

The various borders have different options for setting border widths and colors. See the API notes for details. True border enthusiasts will appreciate that there is also a SoftBevelBorder class for beveled borders with softened corners and that a LineBorder can have rounded corners as well. You can construct these borders only by using one of the class constructors there is no BorderFactory method for them.

Example 9-7. BorderTest.java

  1. import java.awt.*;  2. import java.awt.event.*;  3. import javax.swing.*;  4. import javax.swing.border.*;  5.  6. public class BorderTest  7. {  8.    public static void main(String[] args)  9.    { 10.       BorderFrame frame = new BorderFrame(); 11.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 12.       frame.setVisible(true); 13.    } 14. } 15. 16. /** 17.    A frame with radio buttons to pick a border style. 18. */ 19. class BorderFrame extends JFrame 20. { 21.    public BorderFrame() 22.    { 23.       setTitle("BorderTest"); 24.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 25. 26.       demoPanel = new JPanel(); 27.       buttonPanel = new JPanel(); 28.       group = new ButtonGroup(); 29. 30.       addRadioButton("Lowered bevel", BorderFactory.createLoweredBevelBorder()); 31.       addRadioButton("Raised bevel", BorderFactory.createRaisedBevelBorder()); 32.       addRadioButton("Etched", BorderFactory.createEtchedBorder()); 33.       addRadioButton("Line", BorderFactory.createLineBorder(Color.BLUE)); 34.       addRadioButton("Matte", BorderFactory.createMatteBorder(10, 10, 10, 10, Color .BLUE)); 35.       addRadioButton("Empty", BorderFactory.createEmptyBorder()); 36. 37.       Border etched = BorderFactory.createEtchedBorder(); 38.       Border titled = BorderFactory.createTitledBorder(etched, "Border types"); 39.       buttonPanel.setBorder(titled); 40. 41.       setLayout(new GridLayout(2, 1)); 42.       add(buttonPanel); 43.       add(demoPanel); 44.    } 45. 46.    public void addRadioButton(String buttonName, final Border b) 47.    { 48.       JRadioButton button = new JRadioButton(buttonName); 49.       button.addActionListener(new 50.          ActionListener() 51.          { 52.             public void actionPerformed(ActionEvent event) 53.             { 54.                demoPanel.setBorder(b); 55.                validate(); 56.             } 57.          }); 58.       group.add(button); 59.       buttonPanel.add(button); 60.    } 61. 62.    public static final int DEFAULT_WIDTH = 600; 63.    public static final int DEFAULT_HEIGHT = 200; 64. 65.    private JPanel demoPanel; 66.    private JPanel buttonPanel; 67.    private ButtonGroup group; 68. } 


 javax.swing.BorderFactory 1.2 

  • static Border createLineBorder(Color color)

  • static Border createLineBorder(Color color, int thickness)

    create a simple line border.

  • static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Color color)

  • static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Icon tileIcon)

    create a thick border that is filled with a color or a repeating icon.

  • static Border createEmptyBorder()

  • static Border createEmptyBorder(int top, int left, int bottom, int right)

    create an empty border.

  • static Border createEtchedBorder()

  • static Border createEtchedBorder(Color highlight, Color shadow)

  • static Border createEtchedBorder(int type)

  • static Border createEtchedBorder(int type, Color highlight, Color shadow)

    create a line border with a 3D effect.

    Parameters:

    highlight, shadow

    Colors for 3D effect

     

    type

    One of EtchedBorder.RAISED, EtchedBorder.LOWERED


  • static Border createBevelBorder(int type)

  • static Border createBevelBorder(int type, Color highlight, Color shadow)

  • static Border createLoweredBevelBorder()

  • static Border createRaisedBevelBorder()

    create a border that gives the effect of a lowered or raised surface.

    Parameters:

    type

    One of BevelBorder.LOWERED, BevelBorder.RAISED

     

    highlight, shadow

    Colors for 3D effect


  • static TitledBorder createTitledBorder(String title)

  • static TitledBorder createTitledBorder(Border border)

  • static TitledBorder createTitledBorder(Border border, String title)

  • static TitledBorder createTitledBorder(Border border, String title, int justification, int position)

  • static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font)

  • static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font, Color color)

    Creates a titled border with the specified properties.

    Parameters:

    title

    The title string

     

    border

    The border to decorate with the title

     

    justification

    One of the TitledBorder constants LEFT, CENTER, RIGHT, LEADING, trAILING, or DEFAULT_JUSTIFICATION (left)

     

    position

    One of the TitledBorder constants ABOVE_TOP, TOP, BELOW_TOP, ABOVE_BOTTOM, BOTTOM, BELOW_BOTTOM, or DEFAULT_POSITION (top)

     

    font

    The font for the title

     

    color

    The color of the title


  • static CompoundBorder createCompoundBorder(Border outsideBorder, Border insideBorder)

    combines two borders to a new border.


 javax.swing.border.SoftBevelBorder 1.2 

  • SoftBevelBorder(int type)

  • SoftBevelBorder(int type, Color highlight, Color shadow)

    create a bevel border with softened corners.

    Parameters:

    type

    One of BevelBorder.LOWERED, BevelBorder.RAISED

     

    highlight, shadow

    Colors for 3D effect



 javax.swing.border.LineBorder 1.2 

  • public LineBorder(Color color, int thickness, boolean roundedCorners)

    creates a line border with the given color and thickness. If roundedCorners is true, the border has rounded corners.


 javax.swing.JComponent 1.2 

  • void setBorder(Border border)

    sets the border of this component.

Combo Boxes

If you have more than a handful of alternatives, radio buttons are not a good choice because they take up too much screen space. Instead, you can use a combo box. When the user clicks on the component, a list of choices drops down, and the user can then select one of them (see Figure 9-18).

Figure 9-18. A combo box


If the drop-down list box is set to be editable, then you can edit the current selection as if it were a text field. For that reason, this component is called a combo box it combines the flexibility of a text field with a set of predefined choices. The JComboBox class provides a combo box component.

You call the setEditable method to make the combo box editable. Note that editing affects only the current item. It does not change the content of the list.

You can obtain the current selection or edited text by calling the getSelectedItem method.

In the example program, the user can choose a font style from a list of styles (Serif, SansSerif, Monospaced, etc.). The user can also type in another font.

You add the choice items with the addItem method. In our program, addItem is called only in the constructor, but you can call it any time.

 faceCombo = new JComboBox(); faceCombo.setEditable(true); faceCombo.addItem("Serif"); faceCombo.addItem("SansSerif"); . . . 

This method adds the string at the end of the list. You can add new items anywhere in the list with the insertItemAt method:

 faceCombo.insertItemAt("Monospaced", 0); // add at the beginning 

You can add items of any type the combo box invokes each item's toString method to display it.

If you need to remove items at run time, you use the removeItem or removeItemAt method, depending on whether you supply the item to be removed or its position.

 faceCombo.removeItem("Monospaced"); faceCombo.removeItemAt(0); // remove first item 

The removeAllItems method removes all items at once.

TIP

If you need to add a large number of items to a combo box, the addItem method will perform poorly. Instead, construct a DefaultComboBoxModel, populate it by calling addElement, and then call the setModel method of the JComboBox class.


When the user selects an item from a combo box, the combo box generates an action event. To find out which item was selected, call getSource on the event parameter to get a reference to the combo box that sent the event. Then call the getSelectedItem method to retrieve the currently selected item. You need to cast the returned value to the appropriate type, usually String.

 public void actionPerformed(ActionEvent event) {    label.setFont(new Font(       (String) faceCombo.getSelectedItem(),       Font.PLAIN,       DEFAULT_SIZE)); } 

Example 9-8 shows the complete program.

NOTE

If you want to show a permanently displayed list instead of a dropdown list, use the JList component. We cover JList in Chapter 6 of Volume 2.


Example 9-8. ComboBoxTest.java
  1. import java.awt.*;  2. import java.awt.event.*;  3. import javax.swing.*;  4.  5. public class ComboBoxTest  6. {  7.    public static void main(String[] args)  8.    {  9.       ComboBoxFrame frame = new ComboBoxFrame(); 10.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11.       frame.setVisible(true); 12.    } 13. } 14. 15. /** 16.    A frame with a sample text label and a combo box for 17.    selecting font faces. 18. */ 19. class ComboBoxFrame extends JFrame 20. { 21.    public ComboBoxFrame() 22.    { 23.       setTitle("ComboBoxTest"); 24.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 25. 26.       // add the sample text label 27. 28.       label = new JLabel("The quick brown fox jumps over the lazy dog."); 29.       label.setFont(new Font("Serif", Font.PLAIN, DEFAULT_SIZE)); 30.       add(label, BorderLayout.CENTER); 31. 32.       // make a combo box and add face names 33. 34.       faceCombo = new JComboBox(); 35.       faceCombo.setEditable(true); 36.       faceCombo.addItem("Serif"); 37.       faceCombo.addItem("SansSerif"); 38.       faceCombo.addItem("Monospaced"); 39.       faceCombo.addItem("Dialog"); 40.       faceCombo.addItem("DialogInput"); 41. 42.       // the combo box listener changes the label font to the selected face name 43. 44.       faceCombo.addActionListener(new 45.          ActionListener() 46.          { 47.             public void actionPerformed(ActionEvent event) 48.             { 49.                label.setFont(new Font( 50.                   (String) faceCombo.getSelectedItem(), 51.                   Font.PLAIN, 52.                   DEFAULT_SIZE)); 53.             } 54.          }); 55. 56.       // add combo box to a panel at the frame's southern border 57. 58.       JPanel comboPanel = new JPanel(); 59.       comboPanel.add(faceCombo); 60.       add(comboPanel, BorderLayout.SOUTH); 61.    } 62. 63.    public static final int DEFAULT_WIDTH = 300; 64.    public static final int DEFAULT_HEIGHT = 200; 65. 66.    private JComboBox faceCombo; 67.    private JLabel label; 68.    private static final int DEFAULT_SIZE = 12; 69. } 


 javax.swing.JComboBox 1.2 

  • void setEditable(boolean b)

    Parameters:

    b

    true if the combo box field can be edited by the user, false otherwise


  • void addItem(Object item)

    adds an item to the item list.

  • void insertItemAt(Object item, int index)

    inserts an item into the item list at a given index.

  • void removeItem(Object item)

    removes an item from the item list.

  • void removeItemAt(int index)

    removes the item at an index.

  • void removeAllItems()

    removes all items from the item list.

  • Object getSelectedItem()

    returns the currently selected item.

Sliders

Combo boxes let users choose from a discrete set of values. Sliders offer a choice from a continuum of values, for example, any number between 1 and 100.

The most common way of constructing a slider is as follows:

 JSlider slider = new JSlider(min, max, initialValue); 

If you omit the minimum, maximum, and initial values, they are initialized with 0, 100, and 50, respectively.

Or if you want the slider to be vertical, then use the following constructor call:

 JSlider slider = new JSlider(SwingConstants.VERTICAL, min, max, initialValue); 

These constructors create a plain slider, such as the top slider in Figure 9-19. You will see presently how to add decorations to a slider.

Figure 9-19. Sliders


As the user slides the slider bar, the value of the slider moves between the minimum and the maximum values. When the value changes, a ChangeEvent is sent to all change listeners. To be notified of the change, you call the addChangeListener method and install an object that implements the ChangeListener interface. That interface has a single method, stateChanged. In that method, you should retrieve the slider value:

 public void stateChanged(ChangeEvent event) {    JSlider slider = (JSlider) event.getSource();    int value = slider.getValue();    . . . } 

You can embellish the slider by showing ticks. For example, in the sample program, the second slider uses the following settings:

 slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5); 

The slider is decorated with large tick marks every 20 units and small tick marks every 5 units. The units refer to slider values, not pixels.

These instructions only set the units for the tick marks. To actually have the tick marks appear, you also call

 slider.setPaintTicks(true); 

The major and minor tick marks are independent. For example, you can set major tick marks every 20 units and minor tick marks every 7 units, but you'll get a very messy scale.

You can force the slider to snap to ticks. Whenever the user has finished dragging a slider in snap mode, it is immediately moved to the closest tick. You activate this mode with the call

 slider.setSnapToTicks(true); 

NOTE

The "snap to ticks" behavior doesn't work as well as you might imagine. Until the slider has actually snapped, the change listener still reports slider values that don't correspond to ticks. And if you click next to the slider an action that normally advances the slider a bit in the direction of the click a slider with "snap to ticks" does not move to the next tick.


You can ask for tick mark labels for the major tick marks by calling

 slider.setPaintLabels(true); 

For example, with a slider ranging from 0 to 100 and major tick spacing of 20, the ticks are labeled 0, 20, 40, 60, 80, and 100.

You can also supply other tick marks, such as strings or icons (see Figure 9-19). The process is a bit convoluted. You need to fill a hash table with keys of type Integer and values of type Component. (Autoboxing makes this simple in JDK 5.0 and beyond.) You then call the setLabelTable method. The components are placed under the tick marks. Usually, you use JLabel objects. Here is how you can label ticks as A, B, C, D, E, and F.

 Hashtable<Integer, Component> labelTable = new Hashtable<Integer, Component>(); labelTable.put(0, new JLabel("A")); labelTable.put(20, new JLabel("B")); . . . labelTable.put(100, new JLabel("F")); slider.setLabelTable(labelTable); 

See Chapter 2 of Volume 2 for more information about hash tables.

Example 9-9 also shows a slider with icons as tick labels.

TIP

If your tick marks or labels don't show, double-check that you called setPaintTicks(true) and setPaintLabels(true).


To suppress the "track" in which the slider moves, call

 slider.setPaintTrack(false); 

The fourth slider in Figure 9-19 has no track.

The fifth slider has its direction reversed by a call to

 slider.setInverted(true); 

The example program shows all these visual effects with a collection of sliders. Each slider has a change event listener installed that places the current slider value into the text field at the bottom of the frame.

Example 9-9. SliderTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.util.*;   4. import javax.swing.*;   5. import javax.swing.event.*;   6.   7. public class SliderTest   8. {   9.    public static void main(String[] args)  10.    {  11.       SliderTestFrame frame = new SliderTestFrame();  12.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  13.       frame.setVisible(true);  14.    }  15. }  16.  17. /**  18.    A frame with many sliders and a text field to show slider  19.    values.  20. */  21. class SliderTestFrame extends JFrame  22. {  23.    public SliderTestFrame()  24.    {  25.       setTitle("SliderTest");  26.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  27.  28.       sliderPanel = new JPanel();  29.       sliderPanel.setLayout(new FlowLayout(FlowLayout.LEFT));  30.  31.       // common listener for all sliders  32.       listener = new  33.          ChangeListener()  34.          {  35.             public void stateChanged(ChangeEvent event)  36.             {  37.                // update text field when the slider value changes  38.                JSlider source = (JSlider) event.getSource();  39.                textField.setText("" + source.getValue());  40.             }  41.          };  42.  43.       // add a plain slider  44.  45.       JSlider slider = new JSlider();  46.       addSlider(slider, "Plain");  47.  48.       // add a slider with major and minor ticks  49.  50.       slider = new JSlider();  51.       slider.setPaintTicks(true);  52.       slider.setMajorTickSpacing(20);  53.       slider.setMinorTickSpacing(5);  54.       addSlider(slider, "Ticks");  55.  56.       // add a slider that snaps to ticks  57.  58.       slider = new JSlider();  59.       slider.setPaintTicks(true);  60.       slider.setSnapToTicks(true);  61.       slider.setMajorTickSpacing(20);  62.       slider.setMinorTickSpacing(5);  63.       addSlider(slider, "Snap to ticks");  64.  65.       // add a slider with no track  66.  67.       slider = new JSlider();  68.       slider.setPaintTicks(true);  69.       slider.setMajorTickSpacing(20);  70.       slider.setMinorTickSpacing(5);  71.       slider.setPaintTrack(false);  72.       addSlider(slider, "No track");  73.  74.       // add an inverted slider  75.  76.       slider = new JSlider();  77.       slider.setPaintTicks(true);  78.       slider.setMajorTickSpacing(20);  79.       slider.setMinorTickSpacing(5);  80.       slider.setInverted(true);  81.       addSlider(slider, "Inverted");  82.  83.       // add a slider with numeric labels  84.  85.       slider = new JSlider();  86.       slider.setPaintTicks(true);  87.       slider.setPaintLabels(true);  88.       slider.setMajorTickSpacing(20);  89.       slider.setMinorTickSpacing(5);  90.       addSlider(slider, "Labels");  91.  92.       // add a slider with alphabetic labels  93.  94.       slider = new JSlider();  95.       slider.setPaintLabels(true);  96.       slider.setPaintTicks(true);  97.       slider.setMajorTickSpacing(20);  98.       slider.setMinorTickSpacing(5);  99. 100.       Dictionary<Integer, Component> labelTable = new Hashtable<Integer, Component>(); 101.       labelTable.put(0, new JLabel("A")); 102.       labelTable.put(20, new JLabel("B")); 103.       labelTable.put(40, new JLabel("C")); 104.       labelTable.put(60, new JLabel("D")); 105.       labelTable.put(80, new JLabel("E")); 106.       labelTable.put(100, new JLabel("F")); 107. 108.       slider.setLabelTable(labelTable); 109.       addSlider(slider, "Custom labels"); 110. 111.       // add a slider with icon labels 112. 113.       slider = new JSlider(); 114.       slider.setPaintTicks(true); 115.       slider.setPaintLabels(true); 116.       slider.setSnapToTicks(true); 117.       slider.setMajorTickSpacing(20); 118.       slider.setMinorTickSpacing(20); 119. 120.       labelTable = new Hashtable<Integer, Component>(); 121. 122.       // add card images 123. 124.       labelTable.put(0, new JLabel(new ImageIcon("nine.gif"))); 125.       labelTable.put(20, new JLabel(new ImageIcon("ten.gif"))); 126.       labelTable.put(40, new JLabel(new ImageIcon("jack.gif"))); 127.       labelTable.put(60, new JLabel(new ImageIcon("queen.gif"))); 128.       labelTable.put(80, new JLabel(new ImageIcon("king.gif"))); 129.       labelTable.put(100, new JLabel(new ImageIcon("ace.gif"))); 130. 131.       slider.setLabelTable(labelTable); 132.       addSlider(slider, "Icon labels"); 133. 134.       // add the text field that displays the slider value 135. 136.       textField = new JTextField(); 137.       add(sliderPanel, BorderLayout.CENTER); 138.       add(textField, BorderLayout.SOUTH); 139.    } 140. 141.    /** 142.       Adds a slider to the slider panel and hooks up the listener 143.       @param s the slider 144.       @param description the slider description 145.    */ 146.    public void addSlider(JSlider s, String description) 147.    { 148.       s.addChangeListener(listener); 149.       JPanel panel = new JPanel(); 150.       panel.add(s); 151.       panel.add(new JLabel(description)); 152.       sliderPanel.add(panel); 153.    } 154. 155.    public static final int DEFAULT_WIDTH = 350; 156.    public static final int DEFAULT_HEIGHT = 450; 157. 158.    private JPanel sliderPanel; 159.    private JTextField textField; 160.    private ChangeListener listener; 161. } 


 javax.swing.JSlider 1.2 

  • JSlider()

  • JSlider(int direction)

  • JSlider(int min, int max)

  • JSlider(int min, int max, int initialValue)

  • JSlider(int direction, int min, int max, int initialValue)

    construct a horizontal slider with the given direction and minimum, maximum, and initial values.

    Parameters:

    direction

    One of SwingConstants.HORIZONTAL or SwingConstants.VERTICAL. The default is horizontal.

     

    min, max

    The minimum and maximum for the slider values. Defaults are 0 and 100.

     

    initialValue

    The initial value for the slider. The default is 50.


  • void setPaintTicks(boolean b)

    displays ticks if b is true.

  • void setMajorTickSpacing(int units)

  • void setMinorTickSpacing(int units)

    set major or minor ticks at multiples of the given slider units.

  • void setPaintLabels(boolean b)

    displays tick labels if b is true.

  • void setLabelTable(Dictionary table)

    sets the components to use for the tick labels. Each key/value pair in the table has the form new Integer(value)/component.

  • void setSnapToTicks(boolean b)

    if b is true, then the slider snaps to the closest tick after each adjustment.

  • void setPaintTrack(boolean b)

    if b is true, then a track is displayed in which the slider runs.

The JSpinner Component

A JSpinner is a text field with two small buttons on the side. When the buttons are clicked, the text field value is incremented or decremented (see Figure 9-20).

Figure 9-20. Several variations of the JSpinner component


The values in the spinner can be numbers, dates, values from a list, or, in the most general case, any sequence of values for which predecessors and successors can be determined. The JSpinner class defines standard data models for the first three cases. You can define your own data model to describe arbitrary sequences.

By default, a spinner manages an integer, and the buttons increment or decrement it by 1. You can get the current value by calling the getValue method. That method returns an Object. Cast it to an Integer and retrieve the wrapped value.

 JSpinner defaultSpinner = new JSpinner(); . . . int value = (Integer) defaultSpinner.getValue(); 

You can change the increment to a value other than 1, and you can also supply lower and upper bounds. Here is a spinner with starting value 5, bounded between 0 and 10, and an increment of 0.5:

 JSpinner boundedSpinner = new JSpinner(new SpinnerNumberModel(5, 0, 10, 0.5)); 

There are two SpinnerNumberModel constructors, one with only int parameters and one with double parameters. If any of the parameters is a floating-point number, then the second constructor is used. It sets the spinner value to a Double object.

Spinners aren't restricted to numeric values. You can have a spinner iterate through any collection of values. Simply pass a SpinnerListModel to the JSpinner constructor. You can construct a SpinnerListModel from an array or a class implementing the List interface (such as an ArrayList). In our sample program, we display a spinner control with all available font names.

String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment() .getAvailableFontFamilyNames(); JSpinner listSpinner = new JSpinner(new SpinnerListModel(fonts));

However, we found that the direction of the iteration was mildly confusing because it is opposite from the user experience with a combo box. In a combo box, higher values are below lower values, so you would expect the downward arrow to navigate toward higher values. But the spinner increments the array index so that the upward arrow yields higher values. There is no provision for reversing the traversal order in the SpinnerListModel, but an impromptu anonymous subclass yields the desired result:

 JSpinner reverseListSpinner = new JSpinner(    new SpinnerListModel(fonts)    {       public Object getNextValue()          return super.getPreviousValue();       }       public Object getPreviousValue()       {           return super.getNextValue();       }    }); 

Try out both versions and see which you find more intuitive.

Another good use for a spinner is for a date that the user can increment or decrement. You get such a spinner, initialized with today's date, with the call

 JSpinner dateSpinner = new JSpinner(new SpinnerDateModel()); 

However, if you look carefully at Figure 9-20, you will see that the spinner text shows both date and time, such as

 3/12/02 7:23 PM 

The time doesn't make any sense for a date picker. It turns out to be somewhat difficult to make the spinner show just the date. Here is the magic incantation:

 JSpinner betterDateSpinner = new JSpinner(new SpinnerDateModel()); String pattern = ((SimpleDateFormat) DateFormat.getDateInstance()).toPattern(); betterDateSpinner.setEditor(new JSpinner.DateEditor(betterDateSpinner, pattern)); 

Using the same approach, you can also make a time picker. Then use the SpinnerDateModel constructor that lets you specify a Date, the lower and upper bounds (or null if there are no bounds), and the Calendar field (such as Calendar.HOUR) to be modified.

 JSpinner timeSpinner = new JSpinner(    new SpinnerDateModel(       new GregorianCalendar(2000, Calendar.JANUARY, 1, 12, 0, 0).getTime(),          null, null, Calendar.HOUR)); 

However, if you want to update the minutes in 15-minute increments, then you exceed the capabilities of the standard SpinnerDateModel class.

You can display arbitrary sequences in a spinner by defining your own spinner model. In our sample program, we have a spinner that iterates through all permutations of the string "meat". You can get to "mate", "meta", "team", and another 20 permutations by clicking the spinner buttons.

When you define your own model, you should extend the AbstractSpinnerModel class and define the following four methods:

 Object getValue() void setValue(Object value) Object getNextValue() Object getPreviousValue() 

The getValue method returns the value stored by the model. The setValue method sets a new value. It should throw an IllegalArgumentException if the new value is not appropriate.

CAUTION

The setValue method must call the fireStateChanged method after setting the new value. Otherwise, the spinner field won't be updated.


The getNextValue and getPreviousValue methods return the values that should come after or before the current value, or null if the end of the traversal has been reached.

CAUTION

The getNextValue and getPreviousValue methods should not change the current value. When a user clicks on the upward arrow of the spinner, the getNextValue method is called. If the return value is not null, it is set by a call to setValue.


In the sample program, we use a standard algorithm to determine the next and previous permutations. The details of the algorithm are not important.

Example 9-10 shows how to generate the various spinner types. Click on the Ok button to see the spinner values.

Example 9-10. SpinnerTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.text.*;   4. import java.util.*;   5. import javax.swing.*;   6.   7. /**   8.    A program to test spinners.   9. */  10. public class SpinnerTest  11. {  12.    public static void main(String[] args)  13.    {  14.       SpinnerFrame frame = new SpinnerFrame();  15.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  16.       frame.setVisible(true);  17.    }  18. }  19.  20. /**  21.    A frame with a panel that contains several spinners and  22.    a button that displays the spinner values.  23. */  24. class SpinnerFrame extends JFrame  25. {  26.    public SpinnerFrame()  27.    {  28.       setTitle("SpinnerTest");  29.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  30.       JPanel buttonPanel = new JPanel();  31.       okButton = new JButton("Ok");  32.       buttonPanel.add(okButton);  33.       add(buttonPanel, BorderLayout.SOUTH);  34.  35.       mainPanel = new JPanel();  36.       mainPanel.setLayout(new GridLayout(0, 3));  37.       add(mainPanel, BorderLayout.CENTER);  38.  39.       JSpinner defaultSpinner = new JSpinner();  40.       addRow("Default", defaultSpinner);  41.  42.       JSpinner boundedSpinner = new JSpinner(new SpinnerNumberModel(5, 0, 10, 0.5));  43.       addRow("Bounded", boundedSpinner);  44.  45.       String[] fonts = GraphicsEnvironment  46.          .getLocalGraphicsEnvironment()  47.          .getAvailableFontFamilyNames();  48.  49.       JSpinner listSpinner = new JSpinner(new SpinnerListModel(fonts));  50.       addRow("List", listSpinner);  51.  52.       JSpinner reverseListSpinner = new JSpinner(  53.          new  54.             SpinnerListModel(fonts)  55.             {  56.                public Object getNextValue()  57.                {  58.                   return super.getPreviousValue();  59.                }  60.                public Object getPreviousValue()  61.                {  62.                   return super.getNextValue();  63.                }  64.             });  65.       addRow("Reverse List", reverseListSpinner);  66.  67.       JSpinner dateSpinner = new JSpinner(new SpinnerDateModel());  68.       addRow("Date", dateSpinner);  69.  70.       JSpinner betterDateSpinner = new JSpinner(new SpinnerDateModel());  71.       String pattern = ((SimpleDateFormat) DateFormat.getDateInstance()).toPattern();  72.       betterDateSpinner.setEditor(new JSpinner.DateEditor(betterDateSpinner, pattern));  73.       addRow("Better Date", betterDateSpinner);  74.  75.       JSpinner timeSpinner = new JSpinner(  76.          new SpinnerDateModel(  77.             new GregorianCalendar(2000, Calendar.JANUARY, 1, 12, 0, 0).getTime(),  78.                null, null, Calendar.HOUR));  79.       addRow("Time", timeSpinner);  80.  81.       JSpinner permSpinner = new JSpinner(new PermutationSpinnerModel("meat"));  82.       addRow("Word permutations", permSpinner);  83.    }  84.  85.    /**  86.       Adds a row to the main panel.  87.       @param labelText the label of the spinner  88.       @param spinner the sample spinner  89.    */  90.    public void addRow(String labelText, final JSpinner spinner)  91.    {  92.       mainPanel.add(new JLabel(labelText));  93.       mainPanel.add(spinner);  94.       final JLabel valueLabel = new JLabel();  95.       mainPanel.add(valueLabel);  96.       okButton.addActionListener(new  97.          ActionListener()  98.          {  99.             public void actionPerformed(ActionEvent event) 100.             { 101.                Object value = spinner.getValue(); 102.                valueLabel.setText(value.toString()); 103.             } 104.          }); 105.    } 106. 107.    public static final int DEFAULT_WIDTH = 400; 108.    public static final int DEFAULT_HEIGHT = 250; 109. 110.    private JPanel mainPanel; 111.    private JButton okButton; 112. } 113. 114. /** 115.    A model that dynamically generates word permutations 116. */ 117. class PermutationSpinnerModel extends AbstractSpinnerModel 118. { 119.    /** 120.       Constructs the model. 121.       @param w the word to permute 122.    */ 123.    public PermutationSpinnerModel(String w) 124.    { 125.       word = w; 126.    } 127. 128.    public Object getValue() 129.    { 130.       return word; 131.    } 132. 133.    public void setValue(Object value) 134.    { 135.       if (!(value instanceof String)) 136.          throw new IllegalArgumentException(); 137.       word = (String) value; 138.       fireStateChanged(); 139.    } 140. 141.    public Object getNextValue() 142.    { 143.       int[] codePoints = toCodePointArray(word); 144.       for (int i = codePoints.length - 1; i > 0; i--) 145.       { 146.          if (codePoints[i - 1] < codePoints[i]) 147.          { 148.             int j = codePoints.length - 1; 149.             while (codePoints[i - 1] > codePoints[j]) j--; 150.             swap(codePoints, i - 1, j); 151.             reverse(codePoints, i, codePoints.length - 1); 152.             return new String(codePoints, 0, codePoints.length); 153.          } 154.       } 155.       reverse(codePoints, 0, codePoints.length - 1); 156.       return new String(codePoints, 0, codePoints.length); 157.    } 158. 159.    public Object getPreviousValue() 160.    { 161.       int[] codePoints = toCodePointArray(word); 162.       for (int i = codePoints.length - 1; i > 0; i--) 163.       { 164.          if (codePoints[i - 1] > codePoints[i]) 165.          { 166.             int j = codePoints.length - 1; 167.             while (codePoints[i - 1] < codePoints[j]) j--; 168.             swap(codePoints, i - 1, j); 169.             reverse(codePoints, i, codePoints.length - 1); 170.             return new String(codePoints, 0, codePoints.length); 171.          } 172.       } 173.       reverse(codePoints, 0, codePoints.length - 1); 174.       return new String(codePoints, 0, codePoints.length); 175.    } 176. 177.    private static int[] toCodePointArray(String str) 178.    { 179.       int[] codePoints = new int[str.codePointCount(0, str.length())]; 180.       for (int i = 0, j = 0; i < str.length(); i++, j++) 181.       { 182.          int cp = str.codePointAt(i); 183.          if (Character.isSupplementaryCodePoint(cp)) i++; 184.          codePoints[j] = cp; 185.       } 186.       return codePoints; 187.    } 188. 189.    private static void swap(int[] a, int i, int j) 190.    { 191.       int temp = a[i]; 192.       a[i] = a[j]; 193.       a[j] = temp; 194.    } 195. 196.    private static void reverse(int[] a, int i, int j) 197.    { 198.       while (i < j) { swap(a, i, j); i++; j--; } 199.    } 200. 201.    private String word; 202. } 


 javax.swing.JSpinner 1.4 

  • JSpinner()

    constructs a spinner that edits an integer with starting value 0, increment 1, and no bounds.

  • JSpinner(SpinnerModel model)

    constructs a spinner that uses the given data model.

  • Object getValue()

    gets the current value of the spinner.

  • void setValue(Object value)

    attempts to set the value of the spinner. Throws an IllegalArgumentException if the model does not accept the value.

  • void setEditor(JComponent editor)

    sets the component that is used for editing the spinner value.


 javax.swing.SpinnerNumberModel 1.4 

  • SpinnerNumberModel(int initval, int minimum, int maximum, int stepSize)

  • SpinnerNumberModel(double initval, double minimum, double maximum, double stepSize)

    these constructors yield number models that manage an Integer or Double value. Use the MIN_VALUE and MAX_VALUE constants of the Integer and Double classes for unbounded values.

    Parameters:

    initval

    The initial value

     

    minimum

    The minimum valid value

     

    maximum

    The maximum valid value

     

    stepSize

    The increment or decrement of each spin



 javax.swing.SpinnerListModel 1.4 

  • SpinnerListModel(Object[] values)

  • SpinnerListModel(List values)

    these constructors yield models that select a value from among the given values.


 javax.swing.SpinnerDateModel 1.4 

  • SpinnerDateModel()

    constructs a date model with today's date as the initial value, no lower or upper bounds, and an increment of Calendar.DAY_OF_MONTH.

  • SpinnerDateModel(Date initval, Comparable minimum, Comparable maximum, int step)

    Parameters:

    initval

    The initial value

     

    minimum

    The minimum valid value, or null if no lower bound is desired

     

    maximum

    The maximum valid value, or null if no lower bound is desired

     

    step

    The date field to increment or decrement of each spin. One of the constants ERA, YEAR, MONTH, WEEK_OF_YEAR, WEEK_OF_MONTH, DAY_OF_MONTH, DAY_OF_YEAR, DAY_OF_WEEK, DAY_OF_WEEK_IN_MONTH, AM_PM, HOUR, HOUR_OF_DAY, MINUTE, SECOND, or MILLISECOND of the Calendar class



 java.text.SimpleDateFormat 1.1 

  • String toPattern() 1.2

    gets the editing pattern for this date formatter. A typical pattern is "yyyy-MM-dd". See the JDK documentation for more details about the pattern.


 javax.swing.JSpinner.DateEditor 1.4 

  • DateEditor(JSpinner spinner, String pattern)

    constructs a date editor for a spinner.

    Parameters:

    spinner

    The spinner to which this editor belongs

     

    pattern

    The format pattern for the associated SimpleDateFormat



 javax.swing.AbstractSpinnerModel 1.4 

  • Object getValue()

    gets the current value of the model.

  • void setValue(Object value)

    attempts to set a new value for the model. Throws an IllegalArgumentException if the value is not acceptable. When overriding this method, you should call fireStateChanged after setting the new value.

  • Object getNextValue()

  • Object getPreviousValue()

    compute (but do not set) the next or previous value in the sequence that this model defines.


       
    top



    Core Java 2 Volume I - Fundamentals
    Core Java(TM) 2, Volume I--Fundamentals (7th Edition) (Core Series) (Core Series)
    ISBN: 0131482025
    EAN: 2147483647
    Year: 2003
    Pages: 132

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net