[ Team LiB ] Previous Section Next Section

11.4 Event Handling

In the previous section on layout management, there were a number of examples that arranged JButton components in interesting ways. If you ran the examples, however, you probably noticed that nothing interesting happened when you clicked on the buttons. The fourth step in creating a GUI is hooking up the event handling that makes components respond to user input. As of Java 1.1 and later, AWT and Swing components use the event-handling API defined by the JavaBeans component model. Prior to Java 1.1, the AWT used a different API that is not covered in this chapter. We'll see some examples of event handling using the old model when we study applets (see Chapter 16), where this model is still sometimes used for backward compatibility with old web browsers.

In Java 1.1 and later, the event-handling API is based on events and event listeners. Like everything else in Java, events are objects. An event object is an instance of a class that extends java.util.EventObject. The java.awt.event package defines a number of event classes commonly used by AWT and Swing components. The javax.swing.event package defines additional events used by Swing components, but not by AWT components. And the java.beans package defines a couple of JavaBeans event classes also used by Swing components. Event classes usually define methods (or fields) that provide details about the event that occurred. For example, the java.awt.event.MouseEvent class defines a getX( ) method that returns the X coordinate of the location of the mouse when the event occurred.

An EventListener is an object that is interested in being notified when an event of a particular type occurs. An object that generates events (an event source, such as a JButton component) maintains a list of listeners and provides methods that allow listeners to be added to or removed from this list. When an event of the appropriate type occurs, the event source notifies all registered event listeners. To notify the listeners, it first creates an event object that describes the event and then passes that event object to a method defined by the event listeners. The particular method that is invoked depends on the type of event; different event listener types define different methods.

All event listener types are interfaces that extend the java.util.EventListener interface. This is a marker interface; it does not define any methods of its own, but exists so that you can use the instanceof operator to distinguish event listeners from other object types. The java.awt.event, javax.swing.event, and java.beans packages define a number of event listener interfaces that extend the generic EventListener interface. Each listener is specific to a particular type of event and defines one or more methods that are invoked in particular circumstances (e.g., java.awt.MouseListener). The only special feature of an event listener method is that it always takes an event object as its single argument.

Note that the java.awt.event and javax.swing.event packages define only event listener interfaces, not event listener classes. A GUI must define its own custom implementation of an event listener, as it is this implementation that provides the actual Java code executed in response to an event. These packages do define some Java classes, however: event adapter classes. An event adapter is an implementation of an event listener interface that consists entirely of empty methods. Many listener interfaces define multiple methods, but often you are interested in using only one of these methods at a time. In this case, it is easier to subclass an adapter, overriding only the method you are interested in, instead of implementing the interface directly and having to define all its methods.

Every AWT and Swing application has an automatically created thread, called the event dispatch thread, that invokes the methods of event listeners. When an application starts, the main thread builds the GUI, and then it often exits. From then on, everything that happens takes place in the event dispatch thread, in response to the invocation of event listener methods. Note that AWT and Swing components are not thread-safe, for performance reasons. Thus, if you are writing a multithreaded program, you must be careful to call component methods only from the event dispatch thread. Calling component methods from other threads may cause intermittent and difficult-to-diagnose bugs. If you need to perform some kind of animation or repetitive task with Swing, use a javax.swing.Timer instead of using a separate thread. If another thread really needs to interact with a Swing component, use the java.awt.EventQueue invokeLater( ) and invokeAndWait( ) methods.

Chapter 2 of Java Foundation Classes in a Nutshell describes the event-handling API in more detail and also contains tables that list the various types of AWT and Swing event listeners, their methods, and the components that use those listener interfaces. The following sections contain some simple examples that illustrate different ways of using the Java event-handling API, as well as a more robust example that demonstrates an API for handling certain types of input events using a lower-level API.

11.4.1 Handling Mouse Events

Example 11-10 is a listing of ScribblePane1.java, a simple JPanel subclass that implements the MouseListener and a MouseMotionListener interfaces in order to receive mouse-click and mouse-drag events. It responds to these events by drawing lines, allowing the user to "scribble" in the pane. Figure 11-10 shows the ScribblePane1 example (and a sample scribble) running within the ShowBean program developed in this chapter.

Figure 11-10. A scribble in ScribblePane1
figs/Jex3_1110.gif

Note that the ScribblePane1 class is both the source of mouse events (they are generated by the java.awt.Component superclass) and the event listener. This can be seen most clearly in the constructor method where the component passes itself to its own addMouseListener( ) and addMouseMotionListener( ) methods. The mouseDragged( ) method is the key to scribbling: it uses the drawLine( ) method of the Graphics object to draw a line from the previous mouse position to the current mouse position.

ScribblePane1 does not save the coordinates of the mouse events, and therefore cannot redraw the scribble (after it is obscured by one of the ShowPane menus, for example). ScribblePane1 demonstrates event handling, but it is not a good example of a custom component because it does not implement a paintComponent( ) method to redraw the component as needed. Examples Example 11-11 and Example 11-12 also contain this flaw, which is finally remedied in Example 11-13.

Example 11-10. ScribblePane1.java
package je3.gui;
import javax.swing.*;       // For JPanel component
import java.awt.*;          // For Graphics object
import java.awt.event.*;    // For Event and Listener objects

/**
 * A simple JPanel subclass that uses event listeners to allow the user
 * to scribble with the mouse.  Note that scribbles are not saved or redrawn.
 **/
public class ScribblePane1 extends JPanel
    implements MouseListener, MouseMotionListener {
    protected int last_x, last_y;  // Previous mouse coordinates

    public ScribblePane1( ) {
        // This component registers itself as an event listener for
        // mouse events and mouse motion events.
        this.addMouseListener(this);
        this.addMouseMotionListener(this);

        // Give the component a preferred size
        setPreferredSize(new Dimension(450,200));
    }

    // A method from the MouseListener interface.  Invoked when the
    // user presses a mouse button.
    public void mousePressed(MouseEvent e) {
        last_x = e.getX( );  // remember the coordinates of the click
        last_y = e.getY( );
    }
    
    // A method from the MouseMotionListener interface.  Invoked when the
    // user drags the mouse with a button pressed.
    public void mouseDragged(MouseEvent e) {
        int x = e.getX( );    // Get the current mouse position
        int y = e.getY( );
        // Draw a line from the saved coordinates to the current position
        this.getGraphics( ).drawLine(last_x, last_y, x, y);
        last_x = x;          // Remember the current position
        last_y = y;
    }
    
    // The other, unused methods of the MouseListener interface.
    public void mouseReleased(MouseEvent e) {  }
    public void mouseClicked(MouseEvent e) {  }
    public void mouseEntered(MouseEvent e) {  }
    public void mouseExited(MouseEvent e) {  }
    
    // The other, unused, method of the MouseMotionListener interface.
    public void mouseMoved(MouseEvent e) {  }
}

11.4.2 More Mouse Events

Example 11-11 shows a listing of ScribblePane2.java, which is much like ScribblePane1 except that it uses anonymous inner classes to define its event listeners. This is a common GUI programming idiom in Java, and, in fact, it was one of the motivating factors for adding anonymous inner classes to the language. Note that the inner classes subclass event adapter classes, rather than implement the event listeners directly; this means that unused methods don't have to be implemented.

ScribblePane2 also includes a KeyListener that clears the scribble when the user types the C key, and defines a color property (with setColor( ) and getColor( ) as its property accessor methods) that specifies the color in which to scribble. Recall that the ShowBean program allows you to specify property values. It understands colors specified using hexadecimal RGB notation, so to use this example to scribble with blue lines, you can use a command like this:

% java je3.gui.ShowBean \
  je3.gui.ScribblePane2 color=#0000ff

A final point to note about this example is that the scribbling functionality has been cleaned up and placed into moveto( ), lineto( ), and clear( ) methods. This allows the methods to be invoked by other components and allows the component to be subclassed more cleanly. You can invoke the clear( ) method from the Commands menu of ShowBean.

Example 11-11. ScribblePane2.java
package je3.gui;
import javax.swing.*;       // For JPanel component
import java.awt.*;          // For Graphics object
import java.awt.event.*;    // For Event and Listener objects

/**
 * A simple JPanel subclass that uses event listeners to allow the user
 * to scribble with the mouse.  Note that scribbles are not saved or redrawn.
 **/
public class ScribblePane2 extends JPanel {
    public ScribblePane2( ) {
        // Give the component a preferred size
        setPreferredSize(new Dimension(450,200));

        // Register a mouse event handler defined as an inner class
        // Note the call to requestFocus( ).  This is required in order for
        // the component to receive key events.
        addMouseListener(new MouseAdapter( ) {
                public void mousePressed(MouseEvent e) { 
                    moveto(e.getX( ), e.getY( ));  // Move to click position
                    requestFocus( );              // Take keyboard focus
                }
            });

        // Register a mouse motion event handler defined as an inner class.
        // By subclassing MouseMotionAdapter rather than implementing
        // MouseMotionListener, we only override the method we're interested
        // in and inherit default (empty) implementations of the other methods.
        addMouseMotionListener(new MouseMotionAdapter( ) {
                public void mouseDragged(MouseEvent e) {
                    lineto(e.getX( ), e.getY( ));  // Draw to mouse position
                }
            });

        // Add a keyboard event handler to clear the screen on key 'C'
        addKeyListener(new KeyAdapter( ) {
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode( ) == KeyEvent.VK_C) clear( );
                }
            });
    }
    
    /** These are the coordinates of the the previous mouse position */
    protected int last_x, last_y;

    /** Remember the specified point */
    public void moveto(int x, int y) {
        last_x = x;
        last_y = y;
    }

    /** Draw from the last point to this point, then remember new point */
    public void lineto(int x, int y) {
        Graphics g = getGraphics( );          // Get the object to draw with
        g.setColor(color);                   // Tell it what color to use
        g.drawLine(last_x, last_y, x, y);    // Tell it what to draw
        moveto(x, y);                        // Save the current point
    }

    /**
     * Clear the drawing area, using the component background color.  This
     * method works by requesting that the component be redrawn.  Since this
     * component does not have a paintComponent( ) method, nothing will be
     * drawn.  However, other parts of the component, such as borders or
     * subcomponents, will be drawn correctly.
     **/
    public void clear( ) { repaint( ); }

    /** This field holds the current drawing color property */
    Color color = Color.black; 
    /** This is the property "setter" method for the color property */
    public void setColor(Color color) { this.color = color; }
    /** This is the property "getter" method for the color property */
    public Color getColor( ) { return color; }

}

11.4.3 Handling Component Events

The two previous examples have shown how to handle mouse and keyboard events. These are low-level input events generated by the system and reported to event listeners by code in the java.awt.Component class. Usually, when you are building a GUI, you do not handle these low-level events yourself; instead, you use predefined components to interpret the raw input events and generate higher-level semantic events. For example, when the JButton component detects a low-level mouse click and mouse release, it generates a higher-level java.awt.event.ActionEvent to notify any interested listeners that the user clicked on the button to activate it. Similarly, the JList component generates a javax.swing.event.ListSelectionEvent when the user makes a selection from the list.

Example 11-12 is a listing of ScribblePane3.java. This example extends ScribblePane2 and adds a JButton and a JList to its user interface. The user can clear the screen by clicking on the button and change the drawing color by selecting from the list. You can see these new components in Figure 11-11 (which also demonstrates that the lack of a proper paintComponent( ) method allows ScribblePane3 to scribble on top of its children). The example demonstrates implementing the ActionListener and ListSelectionListener interfaces to respond to the high-level events generated by these Swing components.

Figure 11-11. Swing components in ScribblePane3
figs/Jex3_1111.gif
Example 11-12. ScribblePane3.java
package je3.gui;
import java.awt.*;          // For Graphics object and colors
import javax.swing.*;       // For JPanel component
import java.awt.event.*;    // For ActionListener interface
import javax.swing.event.*; // For ListSelectionListener interface

/**
 * This scribble component includes a JButton to clear the screen, and
 * a JList that lets the user select a drawing color.  It uses
 * event listener objects to handle events from those subcomponents.
 **/
public class ScribblePane3 extends ScribblePane2 {
    // These are colors the user can choose from
    Color[  ] colors = new Color[  ] { Color.black, Color.red, Color.blue };
    // These are names for those colors
    String[  ] colorNames = new String[  ] { "Black", "Red", "Blue" };

    // Add JButton and JList components to the panel.
    public ScribblePane3( ) {
        // Implicit super( ) call here invokes the superclass constructor

        // Add a "Clear" button to the panel.
        // Handle button events with an action listener
        JButton clear = new JButton("Clear");
        clear.addActionListener(new ActionListener( ) {
                public void actionPerformed(ActionEvent e) { clear( ); }
            });
        this.add(clear);

        // Add a JList to allow color choices.
        // Handle list selection events with a ListSelectionListener.
        final JList colorList = new JList(colorNames);
        colorList.addListSelectionListener(new ListSelectionListener( ) {
                public void valueChanged(ListSelectionEvent e) {
                    setColor(colors[colorList.getSelectedIndex( )]);
                }
            });
        this.add(colorList);
    }
}

11.4.4 Low-Level Event Handling

As we just discussed, graphical user interfaces do not usually concern themselves with the details of low-level mouse and keyboard events. Instead, they use predefined components to interpret these events for them. By the same token, predefined components that handle frequent mouse and keyboard events do not usually use the high-level event listener API to handle these low-level events.

Example 11-13 shows a listing of ScribblePane.java, a final reimplementation of our scribble component. This version does not use event listeners at all, but instead overrides the processMouseEvent( ), and processMouseMotionEvent( ), methods defined by its java.awt.Component superclass.[3] Event objects are passed to these methods directly, without any requirement to register event listeners. What is required, however, is that the constructor call enableEvents( ) to specify the kinds of events in which it is interested. If the constructor does not do this, the system will not deliver events of those types, and the various event processing methods will never be invoked. Note that the event processing methods invoke the superclass' implementation for any events they do not handle themselves. This allows the superclass to dispatch these events to any listener objects that may have been registered.

[3] This same technique can be used with the processKeyEvent( ), processFocusEvent( ), processComponentEvent( ), and processWindowEvent methods of Component.

This ScribblePane class also fixes the problem that afflicted the previous three versions: it remembers the user's scribble and implements a paintComponent( ) method to redraw that scribble when necessary. (An application never needs to invoke paintComponent( ): the AWT and Swing infrastructure invoke it as appropriate.) Another enhancement is that ScribblePane uses the Java2D API of Java 1.2 and later to draw wide lines. The lines are drawn using the color specified on the foreground property inherited from JComponent, and you can use ShowBean to experiment with this property.

Example 11-13. ScribblePane.java
package je3.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.List;  // Disambiguate from java.awt.List
import java.util.ArrayList;
import je3.graphics.PolyLine;

/**
 * This custom component allows the user to scribble, and retains the scribbles
 * so that they can be redrawn when needed.  It uses the PolyLine custom Shape
 * implementation defined elsewhere in this book, and demonstrates event
 * handling with low-level event processing methods.
 */
public class ScribblePane extends JComponent {
    List lines;             // The PolyLines that comprise this scribble
    PolyLine currentLine;   // The PolyLine currently being drawn
    Stroke stroke;          // How to draw the lines

    public ScribblePane( ) {
        setPreferredSize(new Dimension(450,200)); // We need a default size
        lines = new ArrayList( );                  // Initialize a list of lines
        stroke = new BasicStroke(3.0f);           // Lines are 3 pixels wide

        // Register interest in mouse button and mouse motion events, so
        // that processMouseEvent( ) and processMouseMotionEvent( ) will be 
        // invoked, even if no event listeners are registered.
        enableEvents(AWTEvent.MOUSE_EVENT_MASK |
                     AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }

    /** We override this method to draw ourselves. */
    public void paintComponent(Graphics g) {
        // Let the superclass do its painting first
        super.paintComponent(g);

        // Make a copy of the Graphics context so we can modify it
        // We cast it at the same time so we can use Java2D graphics
        Graphics2D g2 = (Graphics2D) (g.create( )); 

        // Our superclass doesn't paint the background, so do this ourselves.
        g2.setColor(getBackground( ));
        g2.fillRect(0, 0, getWidth( ), getHeight( ));

        // Set the line width and color to use for the foreground
        g2.setStroke(stroke);
        g2.setColor(this.getForeground( ));

        // Now loop through the PolyLine shapes and draw them all
        int numlines = lines.size( );
        for(int i = 0; i < numlines; i++)
            g2.draw((PolyLine)lines.get(i));
    }

    /**
     * Erase all lines and repaint.  This method is for the convenience of
     * programs that use this component.
     */
    public void clear( ) {
        lines.clear( );
        repaint( );
    }

    /**
     * We override this method to receive notification of mouse button events.
     * See also the enableEvents( ) call in the constructor method.
     */
    public void processMouseEvent(MouseEvent e) {
        // If the type and button are correct, then process it.
        if (e.getButton( ) == MouseEvent.BUTTON1) {
            if (e.getID( ) == MouseEvent.MOUSE_PRESSED) {
                // Start a new line on mouse down
                currentLine = new PolyLine(e.getX( ), e.getY( ));
                lines.add(currentLine);
                e.consume( );
            }
            else if (e.getID( ) == MouseEvent.MOUSE_RELEASED) {
                // End the line on mouse up
                currentLine = null;
                e.consume( );
            }
        }

        // The superclass method dispatches to registered event listeners
        super.processMouseEvent(e);
    }

    /**
     * We override this method to receive notification of mouse motion events.
     */
    public void processMouseMotionEvent(MouseEvent e) {
        if (e.getID( ) == MouseEvent.MOUSE_DRAGGED && // If we're dragging
            currentLine != null) {                   // and a line exists
            currentLine.addSegment(e.getX( ), e.getY( ));  // Add a line segment
            e.consume( );

            // Redraw the whole component.
            // Exercise: optimize this by passing the bounding box
            // of the region that needs redrawing to the repaint( ) method.
            // Don't forget to take line width into account, however.
            repaint( );
        }

        super.processMouseMotionEvent(e);
    }
}

11.4.5 Custom Events and Event Listeners

Although Swing and the AWT define quite a few event classes and event listener interfaces, there is no reason you cannot define custom event and listener types of your own. The class shown in Example 11-14 does exactly that: it defines its own custom event and listener types using inner classes.

The Swing component set provides a number of ways to allow the user to select an item from a list of items. You can present such a choice with a JList component, a JComboBox component, or a group of cooperating JRadioButton components. The APIs for creating, manipulating, and responding to events with these components differ substantially. Example 11-14 is a listing of an ItemChooser class that abstracts away the differences between these three presentation types. When you create an ItemChooser component, you specify the name of the choice being presented, a list of items to be chosen among, the currently chosen item, and a presentation type. The presentation type determines how the choice is presented to the user, but the API you use to work with the ItemChooser component is independent of the presentation.

The ItemChooser class includes an inner class named Demo. ItemChooser.Demo has a main( ) method that demonstrates the ItemChooser component, as shown in Figure 11-12. The demo program gets the choice labels from command-line arguments, so you can run it with a command like the following:

% java je3.gui.ItemChooser\$Demo Fourscore and twenty \
  years ago

Note that on Unix systems, you have to escape the $ in the inner class name with a backslash. On Windows systems, the backslash is not necessary. We'll see another use of ItemChooser in Example 11-17.

Figure 11-12. A demonstration of the ItemChooser component
figs/Jex3_1112.gif

The interesting thing about ItemChooser is that it defines its own event and event listener types as inner classes. You should pay attention to the definitions of these types and study how they are used within the ItemChooser Demo classes, as this example demonstrates both sides of the event architecture: event generation and event handling. This example shows you how to work with JList, JComboBox, and JRadioButton components; it is particularly interesting because it listens for and responds to the events generated by those internal components and translates those internal events into its own event type. Once you understand how ItemChooser works, you'll have a thorough understanding of the AWT and Swing event architecture.

Example 11-14. ItemChooser.java
package je3.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.util.*;

/**
 * This class is a Swing component that presents a choice to the user.  It
 * allows the choice to be presented in a JList, in a JComboBox, or with a
 * bordered group of JRadioButton components.  Additionally, it displays the
 * name of the choice with a JLabel.  It allows an arbitrary value to be
 * associated with each possible choice.  Note that this component only allows
 * one item to be selected at a time.  Multiple selections are not supported.
 **/
public class ItemChooser extends JPanel {
    // These fields hold property values for this component
    String name;           // The overall name of the choice
    String[  ] labels;       // The text for each choice option
    Object[  ] values;       // Arbitrary values associated with each option
    int selection;         // The selected choice
    int presentation;      // How the choice is presented

    // These are the legal values for the presentation field
    public static final int LIST = 1;
    public static final int COMBOBOX = 2;
    public static final int RADIOBUTTONS = 3;

    // These components are used for each of the 3 possible presentations
    JList list;                     // One type of presentation
    JComboBox combobox;             // Another type of presentation
    JRadioButton[  ] radiobuttons;    // Yet another type

    // The list of objects that are interested in our state
    ArrayList listeners = new ArrayList( );

    // The constructor method sets everything up
    public ItemChooser(String name, String[  ] labels, Object[  ] values,
                       int defaultSelection, int presentation)
    {
        // Copy the constructor arguments to instance fields
        this.name = name;
        this.labels = labels;
        this.values = values;
        this.selection = defaultSelection;
        this.presentation = presentation;

        // If no values were supplied, use the labels
        if (values == null) this.values = labels;

        // Now create content and event handlers based on presentation type
        switch(presentation) {
        case LIST: initList( ); break;
        case COMBOBOX: initComboBox( ); break;
        case RADIOBUTTONS: initRadioButtons( ); break;
        }
    }

    // Initialization for JList presentation
    void initList( ) {
        list = new JList(labels);          // Create the list
        list.setSelectedIndex(selection);  // Set initial state
        
        // Handle state changes
        list.addListSelectionListener(new ListSelectionListener( ) {
                public void valueChanged(ListSelectionEvent e) {
                    ItemChooser.this.select(list.getSelectedIndex( ));
                }
            });
        
        // Lay out list and name label vertically
        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); // vertical
        this.add(new JLabel(name));        // Display choice name
        this.add(new JScrollPane(list));   // Add the JList
    }
    
    // Initialization for JComboBox presentation
    void initComboBox( ) {
        combobox = new JComboBox(labels);         // Create the combo box
        combobox.setSelectedIndex(selection);     // Set initial state
        
        // Handle changes to the state
        combobox.addItemListener(new ItemListener( ) {
                public void itemStateChanged(ItemEvent e) {
                    ItemChooser.this.select(combobox.getSelectedIndex( ));
                }
            });
        
        // Lay out combo box and name label horizontally
        this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        this.add(new JLabel(name));
        this.add(combobox);
    }
    
    // Initialization for JRadioButton presentation
    void initRadioButtons( ) {
        // Create an array of mutually exclusive radio buttons
        radiobuttons = new JRadioButton[labels.length];   // the array
        ButtonGroup radioButtonGroup = new ButtonGroup( ); // used for exclusion
        ChangeListener listener = new ChangeListener( ) {  // A shared listener
                public void stateChanged(ChangeEvent e) {
                    JRadioButton b = (JRadioButton)e.getSource( );
                    if (b.isSelected( )) {
                        // If we received this event because a button was
                        // selected, then loop through the list of buttons to
                        // figure out the index of the selected one.
                        for(int i = 0; i < radiobuttons.length; i++) {
                            if (radiobuttons[i] == b) {
                                ItemChooser.this.select(i);
                                return;
                            }
                        }
                    }
                }
            };
        
        // Display the choice name in a border around the buttons
        this.setBorder(new TitledBorder(new EtchedBorder( ), name));
        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        
        // Create the buttons, add them to the button group, and specify
        // the event listener for each one.
        for(int i = 0; i < labels.length; i++) {
            radiobuttons[i] = new JRadioButton(labels[i]);
            if (i == selection) radiobuttons[i].setSelected(true);
            radiobuttons[i].addChangeListener(listener);
            radioButtonGroup.add(radiobuttons[i]);
            this.add(radiobuttons[i]);
        }
    }
    
    // These simple property accessor methods just return field values
    // These are read-only properties.  The values are set by the constructor
    // and may not be changed.
    public String getName( ) { return name; }
    public int getPresentation( ) { return presentation; }
    public String[  ] getLabels( ) { return labels; }
    public Object[  ] getValues( ) { return values; }
    
    /** Return the index of the selected item */
    public int getSelectedIndex( ) { return selection; }
    
    /** Return the object associated with the selected item */
    public Object getSelectedValue( ) { return values[selection]; }
    
    /**
     * Set the selected item by specifying its index.  Calling this
     * method changes the on-screen display but does not generate events.
     **/
    public void setSelectedIndex(int selection) {
        switch(presentation) {
        case LIST: list.setSelectedIndex(selection); break;
        case COMBOBOX: combobox.setSelectedIndex(selection); break;
        case RADIOBUTTONS: radiobuttons[selection].setSelected(true); break;
        }
        this.selection = selection;
    }

    /**
     * This internal method is called when the selection changes.  It stores
     * the new selected index, and fires events to any registered listeners.
     * The event listeners registered on the JList, JComboBox, or JRadioButtons
     * all call this method.
     **/
    protected void select(int selection) {
        this.selection = selection;  // Store the new selected index
        if (!listeners.isEmpty( )) {  // If there are any listeners registered
            // Create an event object to describe the selection
            ItemChooser.Event e =
                new ItemChooser.Event(this, selection, values[selection]);
            // Loop through the listeners using an Iterator
            for(Iterator i = listeners.iterator( ); i.hasNext( );) {
                ItemChooser.Listener l = (ItemChooser.Listener)i.next( );
                l.itemChosen(e);  // Notify each listener of the selection
            }
        }
    }

    // These methods are for event listener registration and deregistration
    public void addItemChooserListener(ItemChooser.Listener l) {
        listeners.add(l);
    }
    public void removeItemChooserListener(ItemChooser.Listener l) {
        listeners.remove(l);
    }

    /**
     * This inner class defines the event type generated by ItemChooser objects
     * The inner class name is Event, so the full name is ItemChooser.Event
     **/
    public static class Event extends java.util.EventObject {
        int selectedIndex;      // index of the selected item
        Object selectedValue;   // the value associated with it
        public Event(ItemChooser source,
                     int selectedIndex, Object selectedValue) {
            super(source);
            this.selectedIndex = selectedIndex;
            this.selectedValue = selectedValue;
        }

        public ItemChooser getItemChooser( ) { return (ItemChooser)getSource( );}
        public int getSelectedIndex( ) { return selectedIndex; }
        public Object getSelectedValue( ) { return selectedValue; }
    }

    /**
     * This inner interface must be implemented by any object that wants to be
     * notified when the current selection in an ItemChooser component changes.
     **/
    public interface Listener extends java.util.EventListener {
        public void itemChosen(ItemChooser.Event e);
    }

    /**
     * This inner class is a simple demonstration of the ItemChooser component
     * It uses command-line arguments as ItemChooser labels and values.
     **/
    public static class Demo {
        public static void main(String[  ] args) {
            // Create a window, arrange to handle close requests
            final JFrame frame = new JFrame("ItemChooser Demo");
            frame.addWindowListener(new WindowAdapter( ) {
                    public void windowClosing(WindowEvent e) {System.exit(0);}
                });

            // A "message line" to display results in
            final JLabel msgline = new JLabel(" ");

            // Create a panel holding three ItemChooser components
            JPanel chooserPanel = new JPanel( );
            final ItemChooser c1 = new ItemChooser("Choice #1", args, null, 0,
                                                   ItemChooser.LIST);
            final ItemChooser c2 = new ItemChooser("Choice #2", args, null, 0,
                                                   ItemChooser.COMBOBOX);
            final ItemChooser c3 = new ItemChooser("Choice #3", args, null, 0,
                                                   ItemChooser.RADIOBUTTONS);
            
            // An event listener that displays changes on the message line
            ItemChooser.Listener l = new ItemChooser.Listener( ) {
                    public void itemChosen(ItemChooser.Event e) {
                        msgline.setText(e.getItemChooser( ).getName( ) + ": " +
                                        e.getSelectedIndex( ) + ": " +
                                        e.getSelectedValue( ));
                    }
                };
            c1.addItemChooserListener(l);
            c2.addItemChooserListener(l);
            c3.addItemChooserListener(l);

            // Instead of tracking every change with an ItemChooser.Listener,
            // applications can also just query the current state when
            // they need it.  Here's a button that does that.
            JButton report = new JButton("Report");
            report.addActionListener(new ActionListener( ) {
                    public void actionPerformed(ActionEvent e) {
                        // Note the use of multiline italic HTML text
                        // with the JOptionPane message dialog box.
                        String msg = "<html><i>" +
                          c1.getName( ) + ": " + c1.getSelectedValue( ) + "<br>"+
                          c2.getName( ) + ": " + c2.getSelectedValue( ) + "<br>"+
                          c3.getName( ) + ": " + c3.getSelectedValue( ) + "</i>";
                        JOptionPane.showMessageDialog(frame, msg);
                    }
                });

            // Add the 3 ItemChooser objects, and the Button to the panel
            chooserPanel.add(c1);
            chooserPanel.add(c2);
            chooserPanel.add(c3);
            chooserPanel.add(report);

            // Add the panel and the message line to the window
            Container contentPane = frame.getContentPane( );
            contentPane.add(chooserPanel, BorderLayout.CENTER);
            contentPane.add(msgline, BorderLayout.SOUTH);
            
            // Set the window size and pop it up.
            frame.pack( );
            frame.show( );
        }
    }
}
    [ Team LiB ] Previous Section Next Section