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.
The following table shows a list of the listener interfaces 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.
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 |
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 |
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 |
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. |
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:
|
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() |
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 //=======================================================// |
/*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 //=======================================================// |
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 |
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 |
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 |
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 |
/*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 //=======================================================// |