Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Swing, New Event Types in Swing

 
Java Programming, Lecture Notes # 85, Revised 06/15/98.

Preface

Students in Prof. Baldwin's Intermediate Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.
 

Introduction

Although event handling using Swing components is the same as event handling using AWT components, the Swing classes provide a number of new event types.

This lesson will briefly discuss all of the new event types, and will provide two different sample programs that illustrate event handling with one of the new event types.

One of the sample programs will also illustrate the important new aspect of Swing wherein every component is also a container.  In this case, we will build a pyramid of Swing JButton objects where each JButton object is contained in the one below it with the bottom JButton object being contained in a JFrame object.

We will then illustrate how these JButton objects respond to action events and ancestor events.  Action events come to us from the AWT whereas ancestor events are new to Swing.
 

New Event Types in Swing

One of the easiest ways to identify the new event types in Swing is to take a look at the listener interfaces defined in Swing.  Another way is to take a look at the event classes defined in  Swing.

The following table shows a list of the listener interfaces defined in the com.sun.java.swing.event package of Swing 1.0.1.
 
  • AncestorListener 
  • CaretListener 
  • CellEditorListener
  • ChangeListener 
  • DocumentEvent 
  • DocumentListener 
  • HyperlinkListener 
  • InternalFrameListener 
  • ListDataListener 
  • ListSelectionListener 
  • MenuListener 
  • PopupMenuListener 
  • TableColumnModelListener 
  • TableModelListener 
  • TreeExpansionListener 
  • TreeModelListener 
  • TreeSelectionListener 
  • UndoableEditListener 
  •  
    The following table shows a list of the event classes defined in the com.sun.java.swing.event package of Swing 1.0.1.

    You might note that there is not an obvious one-to-one correspondence between listener interfaces and event types in every case.  Obviously, if all of the listener interfaces and all of the event classes are included on these two lists, every event class must correspond to an event listener interface in some way.

    I will leave it as an exercise for the student to dig into the documentation and figure out how the event classes relate to the listener interfaces.
     
  • AncestorEvent 
  • CaretEvent 
  • ChangeEvent 
  • EventListenerList 
  • HyperlinkEvent 
  • InternalFrameAdapter 
  • InternalFrameEvent 
  • ListDataEvent 
  • ListSelectionEvent 
  • MenuEvent 
  • PopupMenuEvent 
  • TableColumnModelEvent 
  • TableModelEvent 
  • TreeExpansionEvent 
  • TreeModelEvent 
  • TreeSelectionEvent 
  • UndoableEditEvent 
  •  
    The two sample programs that follow in this lesson illustrate the AncestorEvent class and the AncestorListener interface.
     

    Sample Program 1

    This program illustrates the use of getContentPane() to add a JButton to a JFrame.

    It also illustrates use of AncestorListener on a JButton.

    Running the program and moving the resulting JFrame on the screen produced the following output.  Note that line breaks were manually added to this presentation to make the lines fit in this format.

    Note also that this output doesn't seem to provide a good match for the descriptions and names of two of the methods in the JavaSoft documentation. This will be discussed in more detail later.
     
    Make JFrame visible
    ancestorAdded method invoked
    Event source: com.sun.java.swing.JButton[,0,0,0x0,
                invalid,layout=com.sun.java.swing.OverlayLayout]
    Ancestor: com.sun.java.swing.JButton[,0,0,0x0,
                invalid,layout=com.sun.java.swing.OverlayLayout]
    Parent: com.sun.java.swing.JPanel[null.contentPane,0,0,0x0,
                  invalid,layout=com.sun.java.swing.JRootPane$1]
    Component: com.sun.java.swing.JButton[,0,0,0x0,
                invalid,layout=com.sun.java.swing.OverlayLayout]
    ID value: 1
    ancestorMoved method
    ancestorMoved method
     
    The program was tested using JDK 1.1.6 and Swing 1.0.1 under Win95.
     

    Interesting Code Fragments for Sample Program 1

    We will begin with the import statements that highlight the requirement to import the Swing packages.
     
    import java.awt.*;
    import java.awt.event.*;
    import com.sun.java.swing.*;
    import com.sun.java.swing.event.*;
     
    The main() method in the controlling class is extremely simple, but we will include it here for continuity.
     
    public class SwingEvent10 {
      public static void main(String[] args){
        GUI gui = new GUI();//instantiate a GUI
      }//end main
    }//end class SwingEvent10
     
    That brings us to the class named GUI from which we will instantiate our graphical user interface object and display it on the screen. As you can see below, the constructor for out GUI object is pretty straightforward.

    We begin by instantiating a Swing object of type JFrame, setting its size, giving it a title, etc. We also add a WindowListener to terminate the program when the user closes the JFrame object.

    Then we instantiate a Swing object of type JButton and register an AncestorListener object on the button.  We will discuss the class from which the listener was instantiated shortly.

    After this, we add the JButton object to the to the JFrame object named displayWindow by first invoking the getContentPane() method and then invoking the add() method on the content pane.  We will discuss this further following the listing of the fragment.

    Finally, we display a message and make the JFrame object visible and that concludes the constructor.
     
    class GUI {
      public GUI(){//constructor
        JFrame displayWindow = new JFrame();
        displayWindow.setSize(300,300);
        displayWindow.setTitle("Copyright 1998, R.G.Baldwin");
    
        displayWindow.addWindowListener(new WProc1());
        
        JButton theButton = new JButton("Button");
        
        theButton.addAncestorListener(new MyAncestorListener());
        
        displayWindow.getContentPane().add(theButton);
    
        System.out.println("Make JFrame visible");
        displayWindow.setVisible(true);    
      }//end constructor
     
    Now what about this getContentPane() method.  This is something that doesn't exist in the AWT.

    In short, in the AWT, we add components to, and otherwise manipulate, the client area of a Frame object directly.

    However, in Swing, some "panes" are automatically placed in the client area of a JFrame object, and we add components to, and otherwise manipulate, those panes instead of manipulating the client area of the JFrame object directly.

    Rather than to try to explain this in my own words, I am simply going to provide a quotation from the JavaSoft documentation for the JFrame object, Swing, Version 1.0.1. Note that the following wording is the copyrighted property of JavaSoft. The emphasis was added by me.
     
     
    public class JFrame 
    extends Frame 
    implements WindowConstants, Accessible, RootPaneContainer 

    An extended version of java.awt.Frame that adds support for interposing input and painting behavior in front of the frames children (see glassPane), support for special children that are managed by a LayeredPane (see rootPane) and for Swing MenuBars

    The JFrame class is slightly incompatible with java.awt.Frame. JFrame contains a JRootPane as it's only child. The contentPane should be the parent of any children of the JFrame. This is different than java.awt.Frame, e.g. to add a child to an AWT Frame you'd write: 

           frame.add(child); 
     

    However using JFrame you need to add the child to the JFramescontentPane instead: 

           frame.getContentPane().add(child); 
     

    The same is true for setting LayoutManagers, removing components, listing children, etc. All these methods should normally be sent to the contentPane() instead of the JFrame itself. The contentPane() will always be non-null. Attempting to set it to null will cause the JFrame to throw an exception. The default contentPane() will have a BorderLayout manager set on it. 

    Please see the JRootPane documentation for a complete description of the contentPane, glassPane, and layeredPane properties. 

    Warning: serialized objects of this class will not be compatible with future swing releases. The current serialization support is appropriate for short term storage or RMI between Swing1.0 applications. It will not be possible to load serialized Swing1.0 objects with future releases of Swing. The JDK1.2 release of Swing will be the compatibility baseline for the serialized form of Swing objects. 

     
    For the time being, and for this simple example, this all boils down to a requirement to insert the method call

    getContentPane();

    between the reference to the JFrame object and calls to add(), setLayout(), etc.  For more complex programs, the ramifications could be more significant.

    Our GUI class has two inner classes.  One of those is a WindowListener class that is used to terminate the program when the user closes the JFrame object.  It is so simple and so common that I'm not going to show it here.  You can see it in the complete listing of the program that follows later if you are interested.

    The second inner class (and these could just as well be implemented as top-level classes instead of inner classes) is used to instantiate an AncestorListener object to be registered on the JButton object.

    This is a little more interesting.  The AncestorListener interface declares three methods, and as far as I know there is no adapter for this interface.  Therefore, our class that implements the interface must define all three methods.

    A brief description of each of the three methods follows:
     
    • ancestorAdded(AncestorEvent)  -- Called when the source or one of its ancestors is made visible either by setVisible(true) being called or by its being added to the component hierarchy. 
    • ancestorMoved(AncestorEvent)  -- Called when either the source or one of its ancestors is moved. 
    • ancestorRemoved(AncestorEvent)  --  Called when the source or one of its ancestors is made invisible either by setVisible(false) being called or by its being remove from the component hierarchy. 
     
    As you can see, whenever one of the methods is called, an object of type AncestorEvent is passed as a parameter.  As usual, it is possible to invoke the methods of the AncestorEvent class and the classes extended by that class to learn more about the event.

    We will define all three of the interface methods (as required) in our class definition.  The following fragment shows only the definition of the first of the three methods.

    When this method is called, it invokes five different methods of the incoming AncestorEvent object and displays the material returned from those methods.  The output from invoking these methods was shown earlier in this lesson.

    As mentioned earlier, the output doesn't seem to provide a good match for the descriptions and names of the getAncestor() and getAncestorParent() methods in the JavaSoft documentation.  The output seems to refer to the JButton object as the ancestor and the JRootPane as the parent of the ancestor.  It would seem from a cursory examination that the JButton object is a child, not an ancestor, but this must depend on the interpretation of the class library in some fashion.

    In any event, since there isn't any good documentation available to research this anomaly at the time of this writing, I am simply going to mark it up as something that needs to be looked into later when better documentation becomes available.
     
      class MyAncestorListener implements AncestorListener{
        
        public void ancestorAdded(AncestorEvent e){
          System.out.println("ancestorAdded method invoked");
          System.out.println("Event source: " + e.getSource());
          System.out.println("Ancestor: " + e.getAncestor());
          System.out.println("Parent: " + e.getAncestorParent());
          System.out.println("Component: " + e.getComponent());
          System.out.println("ID value: " + e.getID());
        }//end ancestorAdded()
     
    The final code fragment shows the definitions of the other two methods of the AncestorListener interface.

    If you compile and run this program and observe the output as the program runs, you will see that the ancestorAdded() method and the ancestorMoved() method are both called when the JFrame object is made visible.

    Following this, whenever the JFrame object is moved on the screen, the ancestorMoved() method will be called.

    Iconifying and then deiconifying the JFrame object also caused the ancestorMoved() method to be called.

    At no time during my experiments was the ancestorRemoved() method called.
     
          
        public void ancestorRemoved(AncestorEvent e){
          System.out.println("ancestorRemoved method");
        }//end ancestorRemoved()
          
        public void ancestorMoved(AncestorEvent e){
          System.out.println("ancestorMoved method");
        }//end ancestorMoved
      }//end class MyAncestorListener
      //.....................................................//
    }//end class GUI definition
    //=======================================================//
    .

    Program Listing for Sample Program 1

    This section contains a complete listing of the program.  Discussion of the second program begins immediately following this listing.
     
    /*File SwingEvent10.java Copyright 1998, R.G.Baldwin
    Illustrates use of getContentPane() to add a JButton to
    a JFrame.
    
    Illustrates use of AncestorListener on a JButton.
    
    Running the program and moving the resulting JFrame on the
    screen produced the following output.  Note that line breaks
    were manually added to this presentation to make the lines
    fit in this format. Note that these outputs don't seem to
    provide a good match for the descriptions and names of the
    methods in the JavaSoft documentation.
    
    Make JFrame visible
    ancestorAdded method invoked
    Event source: com.sun.java.swing.JButton[,0,0,0x0,
                invalid,layout=com.sun.java.swing.OverlayLayout]
    Ancestor: com.sun.java.swing.JButton[,0,0,0x0,
                invalid,layout=com.sun.java.swing.OverlayLayout]
    Parent: com.sun.java.swing.JPanel[null.contentPane,0,0,0x0,
                  invalid,layout=com.sun.java.swing.JRootPane$1]
    Component: com.sun.java.swing.JButton[,0,0,0x0,
                invalid,layout=com.sun.java.swing.OverlayLayout]
    ID value: 1
    ancestorMoved method
    ancestorMoved method
    
    Tested using JDK 1.1.6 and Swing 1.0.1 under Win95.
    **********************************************************/
    import java.awt.*;
    import java.awt.event.*;
    import com.sun.java.swing.*;
    import com.sun.java.swing.event.*;
    
    public class SwingEvent10 {
      public static void main(String[] args){
        GUI gui = new GUI();//instantiate a GUI
      }//end main
    }//end class SwingEvent10
    //=======================================================//
    
    
    //The following class is used to instantiate a 
    // graphical user interface object.
    class GUI {
      public GUI(){//constructor
        //Create a new JFrame object, set size, title, etc.
        JFrame displayWindow = new JFrame();
        displayWindow.setSize(300,300);
        displayWindow.setTitle("Copyright 1998, R.G.Baldwin");
        //Add window listener to terminate the program
        displayWindow.addWindowListener(new WProc1());
        
        //Create a JButton object
        JButton theButton = new JButton("Button");
        
        //Register an AncestorListener object on the JButton
        theButton.addAncestorListener(new MyAncestorListener());
        
        //Add the JButton to the JFrame using content pane
        displayWindow.getContentPane().add(theButton);
    
        System.out.println("Make JFrame visible");
        displayWindow.setVisible(true);    
      }//end constructor
      //.....................................................//
      //Begin inner class definitions
      //The following listener is used to terminate the program
      // when the user closes the frame.
      class WProc1 extends WindowAdapter{
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing()
      }//end class WProc1
      //.....................................................//
    
      //Define an AncestorListener class
      class MyAncestorListener implements AncestorListener{
        //Define three methods declared in AncestorListener
        // interface.
        
        public void ancestorAdded(AncestorEvent e){
          System.out.println("ancestorAdded method invoked");
          System.out.println("Event source: " + e.getSource());
          System.out.println("Ancestor: " + e.getAncestor());
          System.out.println("Parent: " + e.getAncestorParent());
          System.out.println("Component: " + e.getComponent());
          System.out.println("ID value: " + e.getID());
        }//end ancestorAdded()
          
        public void ancestorRemoved(AncestorEvent e){
          System.out.println("ancestorRemoved method");
        }//end ancestorRemoved()
          
        public void ancestorMoved(AncestorEvent e){
          System.out.println("ancestorMoved method");
        }//end ancestorMoved
      }//end class MyAncestorListener
      //.....................................................//
    }//end class GUI definition
    //=======================================================//
    .
     

    Sample Program 2

    This program further illustrates the use of an AncestorListener on a JButton.

    More importantly, this program illustrates the very important fact that JButton objects are containers that can contain other objects including other JButton objects.

    This program stacks three JButton objects on top of one another with the stack of three JButton objects being placed on a JFrame object. ActionListener objects are registered on each of the buttons to trap an actionPerformed() event when the button is clicked and to display the source of the event.

    AncestorListener objects are also registered on all three of the JButton objects.

    Running the program and carefully clicking each of the three buttons in succession from the top of the stack to the bottom of the stack, and then moving the JFrame object on the screen produces the following output. Note that some blank lines were manually inserted to make it easier to follow this material.
     
    Make JFrame visible
    In ancestorAdded method
    Event source: First Button
    In ancestorAdded method
    Event source: Second Button
    In ancestorAdded method
    Event source: Third Button
    
    In ancestorMoved method
    Event source: First Button
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
    
    In ancestorMoved method
    Event source: First Button
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
    
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
    In ancestorMoved method
    Event source: Third Button
    
    In actionPerformed method
    Event source: Third Button
    In actionPerformed method
    Event source: Second Button
    In actionPerformed method
    Event source: First Button
    
    In ancestorMoved method
    Event source: First Button
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
     
    The output produced by clicking the buttons is highlighted in boldface in the above output.
     
    This program was tested using JDK 1.1.6 and Swing 1.0.1 under Win95.

    Interesting Code Fragments for Sample Program 2

     The controlling class and the main() method for this program is the same as the previous program, so it has been omitted for brevity.

    Much of the code in the constructor for the GUI class is also the same as in the previous program so I have deleted it from the following listing for brevity.

    Note the use of getContentPane() when setting the layout manager as described earlier.

    Three JButton objects are instantiated.  Then the three buttons are stacked by adding secondButton to firstButton, and by adding thirdButton to secondButton.

    An AncestorListener object is registered on all three of the buttons and then an ActionListener object is registered on all three of the buttons.

    The remainder of the constructor was the same as before and was removed for brevity.
     
    class GUI {
      public GUI(){//constructor
        //...snip
    
        displayWindow.getContentPane().setLayout(
                                             new FlowLayout());
        //...snip
    
        JButton firstButton = new JButton("First Button");
        JButton secondButton = new JButton("Second Button");
        JButton thirdButton = new JButton("Third Button");
        
        firstButton.add(secondButton);
        secondButton.add(thirdButton);
        
        firstButton.addAncestorListener(
                                     new MyAncestorListener());
        secondButton.addAncestorListener(
                                     new MyAncestorListener());
        thirdButton.addAncestorListener(
                                     new MyAncestorListener());
        
        firstButton.addActionListener(new MyActionListener());
        secondButton.addActionListener(new MyActionListener());
        thirdButton.addActionListener(new MyActionListener());
        
        //...snip
      }//end constructor
     
    The AncestorListener class was very similar to the previous version, so the definition for only one of the methods is shown in the following fragment.

    Note the requirement for downcasting in this version of the method. This is because invocation of the getSource() method returns an object of type Object and it must be downcast to type JButton to be useful in this case.
     
      class MyAncestorListener implements AncestorListener{
        public void ancestorAdded(AncestorEvent e){
          System.out.println("In ancestorAdded method");
          System.out.println("Event source: " + 
                  ((JButton)e.getSource()).getActionCommand());
        }//end ancestorAdded()
          
        //...snip
    
      }//end class MyAncestorListener
     
    Finally we see the ActionListener class that traps action events on the buttons when they are clicked and presents appropriate output on the screen.
     
      class MyActionListener implements ActionListener{
        public void actionPerformed(ActionEvent e){
          System.out.println("In actionPerformed method");
          System.out.println("Event source: " + 
                  ((JButton)e.getSource()).getActionCommand());
        }//end actionPerformed()
      }//end class MyActionListener
    .

    Program Listing for Sample Program 2

    This section contains a complete listing of the program.
     
    /*File SwingEvent11.java Copyright 1998, R.G.Baldwin
    Further illustrates use of AncestorListener on a JButton.
    Also see SwingEvent10.java.
    
    Illustrates that JButton objects are containers that 
    can contain other JButton objects.
    
    This program stacks three JButton objects on top of one
    another with the stack of three JButton objects being
    placed on a JFrame object.
    
    Running the program and carefully clicking each of the
    three buttons in succession from the top of the stack to
    the bottom of the stack, and then moving the JFrame object
    on the screen produces the following output. Note that
    some blank lines were manually inserted to make it easier
    to follow this material.
    
    Make JFrame visible
    In ancestorAdded method
    Event source: First Button
    In ancestorAdded method
    Event source: Second Button
    In ancestorAdded method
    Event source: Third Button
    
    In ancestorMoved method
    Event source: First Button
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
    
    In ancestorMoved method
    Event source: First Button
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
    
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
    In ancestorMoved method
    Event source: Third Button
    
    In actionPerformed method
    Event source: Third Button
    In actionPerformed method
    Event source: Second Button
    In actionPerformed method
    Event source: First Button
    
    In ancestorMoved method
    Event source: First Button
    In ancestorMoved method
    Event source: Second Button
    In ancestorMoved method
    Event source: Third Button
    
    
    Tested using JDK 1.1.6 and Swing 1.0.1 under Win95.
    **********************************************************/
    import java.awt.*;
    import java.awt.event.*;
    import com.sun.java.swing.*;
    import com.sun.java.swing.event.*;
    
    public class SwingEvent11 {
      public static void main(String[] args){
        GUI gui = new GUI();//instantiate a GUI
      }//end main
    }//end class SwingEvent11
    //=======================================================//
    
    //The following class is used to instantiate a 
    // graphical user interface object.
    class GUI {
      public GUI(){//constructor
        //Create a new JFrame object, set size, title, etc.
        JFrame displayWindow = new JFrame();
        displayWindow.setSize(300,100);
        displayWindow.setTitle("Copyright 1998, R.G.Baldwin");
        
        //Note required use of getContentPane() in following
        // statement.
        displayWindow.getContentPane().setLayout(
                                             new FlowLayout());
        //Add window listener to terminate the program
        displayWindow.addWindowListener(new WProc1());
        
        //Create three JButton objects
        JButton firstButton = new JButton("First Button");
        JButton secondButton = new JButton("Second Button");
        JButton thirdButton = new JButton("Third Button");
        
        //Stack the three JButton objects on top of one
        // another.
        firstButton.add(secondButton);
        secondButton.add(thirdButton);
        
        //Register an AncestorListener object on each JButton
        firstButton.addAncestorListener(
                                     new MyAncestorListener());
        secondButton.addAncestorListener(
                                     new MyAncestorListener());
        thirdButton.addAncestorListener(
                                     new MyAncestorListener());
        
        //Register an ActionListener object on each JButton
        firstButton.addActionListener(new MyActionListener());
        secondButton.addActionListener(new MyActionListener());
        thirdButton.addActionListener(new MyActionListener());
        
        //Add the JButton to the JFrame using content pane
        displayWindow.getContentPane().add(firstButton);
    
        System.out.println("Make JFrame visible");
        displayWindow.setVisible(true);    
      }//end constructor
      //.....................................................//
      //Begin inner class definitions
    
      //The following listener is used to terminate the 
      // program when the user closes the frame.
      class WProc1 extends WindowAdapter{
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing()
      }//end class WProc1
      //.....................................................//
    
      //Define an AncestorListener class
      class MyAncestorListener implements AncestorListener{
        //Define three methods declared in AncestorListener
        // interface.  Note the required downcasting.
        
        public void ancestorAdded(AncestorEvent e){
          System.out.println("In ancestorAdded method");
          System.out.println("Event source: " + 
                  ((JButton)e.getSource()).getActionCommand());
        }//end ancestorAdded()
          
        public void ancestorRemoved(AncestorEvent e){
          System.out.println("In ancestorRemoved method");
          System.out.println("Event source: " + 
                  ((JButton)e.getSource()).getActionCommand());
        }//end ancestorRemoved()
          
        public void ancestorMoved(AncestorEvent e){
          System.out.println("In ancestorMoved method");
          System.out.println("Event source: " + 
                  ((JButton)e.getSource()).getActionCommand());
    
        }//end ancestorMoved
      }//end class MyAncestorListener
      //.....................................................//
    
      //Define an ActionListener class
      class MyActionListener implements ActionListener{
        public void actionPerformed(ActionEvent e){
          System.out.println("In actionPerformed method");
          System.out.println("Event source: " + 
                  ((JButton)e.getSource()).getActionCommand());
        }//end actionPerformed()
      }//end class MyActionListener
      //.....................................................//
    
    }//end class GUI definition
    //=======================================================//
    .
    -end-