Skip to main content
Logo image

Java, Java, Java: Object-Oriented Problem Solving, 2024E

Section 12.9 From the Java Library: javax.swing.JComboBox

JComboBox is a Swing component that combines a text field and a drop-down list (Figure 12.9.1). It lets the user either type in a selection or choose a selection from a list that appears when the user requests it—a JComboBox’s drop-down behavior is somewhat similar to a java.awt.Choice box.
Figure 12.9.1. The JComboBox component.
A JComboBox can be used to represent a drop-down menu. When the user clicks on a JComboBox, a list of options drops down, and the user can select a particular option that is stored in the box’s internal state (Figure 12.9.2).
Figure 12.9.2. Using a JComboBox.
The list of options associated with a JComboBox can be built beforehand and inserted into the component in a constructor, or items can be inserted one at a time by repeatedly using its addItem() method.
As Figure 12.9.1 shows, either an array or a vector of items can be passed to a constructor method to initialize the box’s menu. The items stored in a JComboBox box are references to Objects, most commonly Strings that represent the name of the menu item. They are stored in the (zero indexed) order in which they are added. The addItem() method is used to add an individual Object to a JComboBox. By default, the first item added to a JComboBox will be the selected item until the user selects another item.
When the user makes a selection in a JComboBox, the item selected can be gotten either by its reference (getSelectedItem()) or by its position within the menu (getSelectedIndex()). There are also methods to setSelectedItem() and setSelectedIndex() that let you select an individual item either by its reference or its position. The addItemListener() method is used to designate some object as the listener for the ItemEvent s that are generated whenever the user selects a menu option. Alternatively, the addActionListener() method lets you handle action events, such as when the user types a value into the box.

Subsection 12.9.1 A JComboBoxExample

As a simple example, let’s design an graphical interface that can be used to display the fractal patterns we developed earlier. We want an interface that lets the user select from among the available patterns—we’ll use the Sierpinski gasket and nested boxes for starters. In addition, the user should also be able to select different levels for the drawings, from 0 to 9. We want to present these options in two menus, with one JComboBox for each menu.
The first step is to declare and instantiate the JComboBoxes as instance variables:
private String items[] =
    {"Sierpinski Gasket","Nested Boxes"};
private JComboBox<String> patterns = new JComboBox<>(items);
private JComboBox<String> levels = new JComboBox<>();
Note that in this case we pass the constructor for the patterns menu an entire array of items. If we hadn’t done it this way, we would add individual items to the combo box in the JFrame’s constructor RecursivePatterns(). In fact, that’s how we’ll initialize the levels menu:
for (int k=0; k < 10; k++)   // Add 10 levels
    levels.addItem(k + "" );
levels.setSelectedItem("4"); // Select default level
This loop would be placed in the JFrame’s constructor, RecursivePatterns(). It adds strings representing levels 0 to 9 to the menu and initializes the box so that level four is showing as the default option.
Our next step is to designate the JFrame as the ItemListener for both menus—that is, the JFrame is named as the object that will handle the events that occur in the JComboBoxes. Then we add the JComboBox component to the JFrame:
controls.add(levels);    // Control panel for menus
controls.add(patterns);
                               
getContentPane().add(controls, "North");  // Add the controls
getContentPane().add(canvas, "Center");   // And drawing panel
              
levels.addItemListener(this);   // Register menus with a listener
patterns.addItemListener(this);
Note that we use a separate controls panel (a JPanel) for the two menus and a canvas panel (another JPanel) for the drawings.
The next step is to implement the itemStateChanged() method to handle the user’s selections. Whenever the user selects an item from a JComboBox menu, an ItemEvent is generated. In order to handle these events, the program must implement the ItemListener interface, which consists of the single method itemStateChanged(). This method is invoked automatically whenever the user selects an item from one of the JComboBox es:
public void itemStateChanged(ItemEvent e) {
    canvas.setPattern(patterns.getSelectedIndex(),
                         levels.getSelectedIndex());
    repaint();
}
The itemStateChanged() method has the same general form as the actionPerformed() method, except that its parameter is an ItemEvent . For this example, the program uses the getSelectedIndex() method to get the selected pattern and the selected level by their respective item numbers within the menus. It then passes these values along to the canvas object, which takes care of the drawing. Finally, the method invokes the repaint() method. Because the JFrame is a container, this will cause all of its components to be repainted as well.
The complete implementation for the program is given in Listing 12.9.3.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class RecursivePatterns extends JFrame implements ItemListener  {
  private String choices[] = {"Sierpinski Gasket", "Nested Boxes"};
  private JComboBox<String> patterns = new JComboBox<>(choices); // Pattern choices
  private JComboBox<String> levels = new JComboBox<>();          // Level choices
  private Canvas canvas = new Canvas();                // Drawing panel
  private JPanel controls = new JPanel();

  public RecursivePatterns() {
    for (int k=0; k < 10; k++)            // Add 10 levels
      levels.addItem(k + "" );
    patterns.setSelectedItem(choices[0]); // Initialize menus
    levels.setSelectedItem("4");
    canvas.setBorder(BorderFactory.createTitledBorder("Drawing Canvas"));
    controls.add(levels);         // Control panel for menus
    controls.add(patterns);
    getContentPane().add(controls,"North"); // Add controls
    getContentPane().add(canvas,"Center");  // Add drawing panel
    levels.addItemListener(this);   // Register menus with listener
    patterns.addItemListener(this);
    setSize(canvas.WIDTH,canvas.HEIGHT+controls.getSize().width);
  } // init()

  public void itemStateChanged(ItemEvent e) {
    canvas.setPattern(patterns.getSelectedIndex(),
                                     levels.getSelectedIndex());
    repaint();                             // Repaint the JFrame
  } // itemStateChanged()
  
  public static void main(String args[]) {
      JFrame f = new RecursivePatterns();
      f.setVisible(true);
   }
   } // RecursivePatterns
Listing 12.9.3. The RecursivePatterns program.
Figure 12.9.4. Design of a drawing Canvas class.
The actual drawing of the fractal patterns is handled by the canvas JPanel component, whose design is shown in Figure 12.9.4 and whose implementation is given in Figure 12.9.5. All of the drawing is done in the paintComponent() method. Because the canvas is contained within the JFrame, the paintComponent() method is called automatically whenever the JFrame repaints itself. Notice how the switch statement uses the pattern that the user chose to call the corresponding drawing method. You can see from this switch statement that a JComboBox’s items are zero indexed.
import javax.swing.*;
import java.awt.*;

public class Canvas extends JPanel {
  private static final int GASKET = 0, BOXES = 1;
  public static final int WIDTH=400, HEIGHT=400;
  private final int HBOX=10, VBOX=50, BOXSIDE=200, BOXDELTA=10;
  private final int gP1X = 10;  private final int gP1Y = 280; // Initial
  private final int gP2X = 290; private final int gP2Y = 280; // gasket
  private final int gP3X = 150; private final int gP3Y = 110; // points
  private int pattern = 0 ;                      // Current pattern
  private int level = 4;                         // Current level

  public Canvas() {
    setSize(WIDTH, HEIGHT);
  }
  public void setPattern(int pat, int lev) {
    pattern = pat;
    level = lev;
  }

  public void paintComponent(Graphics g) {
        g.setColor(getBackground());   // Redraw the panel's background
        g.drawRect(0, 0, WIDTH, HEIGHT);
        g.setColor(getForeground());
        switch (pattern) {
        case GASKET:
            drawGasket(g, level, gP1X, gP1Y, gP2X, gP2Y, gP3X, gP3Y);
            break;
        case BOXES:
            drawBoxes(g, level, HBOX, VBOX, BOXSIDE, BOXDELTA );
            break;
        } // switch
    } // paintComponent()
  
   /** drawGasket()---recursively draws the Sierpinski
    *  gasket pattern, with points (p1X, p1Y), (p2X, p2Y), (p3X, p3Y)
    *  representing the vertices of its enclosing triangle.
    * level (>= 0) is the recursion parameter (base case: level  0)
    */
    private void drawGasket(Graphics g, int lev, int p1X, int p1Y,
                   int p2X, int p2Y, int p3X, int p3Y) {
        g.drawLine(p1X, p1Y, p2X, p2Y);  // Draw a triangle
        g.drawLine(p2X, p2Y, p3X, p3Y);
        g.drawLine(p3X, p3Y, p1X, p1Y);
        if (lev > 0) { // If more levels, draw 3 smaller gaskets
            int q1X = (p1X + p2X) / 2;    int q1Y = (p1Y + p2Y) / 2;
            int q2X = (p1X + p3X) / 2;    int q2Y = (p1Y + p3Y) / 2;
            int q3X = (p2X + p3X) / 2;    int q3Y = (p2Y + p3Y) / 2;
            drawGasket(g, lev - 1, p1X, p1Y, q1X, q1Y, q2X, q2Y);
            drawGasket(g, lev - 1, p2X, p2Y, q1X, q1Y, q3X, q3Y);
            drawGasket(g, lev - 1, p3X, p3Y, q2X, q2Y, q3X, q3Y);
        }
    } // drawGasket()
  
   /** drawBoxes()---recursively draws pattern of nested squares
    *  with (locX, locY) the top left corner of outer the square and
    *  side being the length square's side.
    * level (>= 0) is the recursion parameter (base case: level  0)
    * delta is used to adjust the length of the side.
    */
    private void drawBoxes(Graphics g, int level,
           int locX, int locY, int side, int delta) {
        g.drawRect(locX, locY, side, side );
        if (level > 0) {
            int newLocX = locX + delta; int newLocY = locY + delta;
            drawBoxes(g, level - 1, newLocX, newLocY,
                                          side - 2 * delta, delta);
        }
    } // drawBoxes()
} // Canvas
Listing 12.9.5. The Canvas class is a drawing panel.

Exercises Self-study Exercises

1. Recursive Patterns.
  1. Write a revised drawBoxes() method that reduces the length of the side at each level by a fixed ratio of the length of the side, for example, 10 percent.
  2. Add your revised method to the RecursivePatterns program below as a third pattern.
You have attempted of activities on this page.