This lesson was originally written in March of 1998 using the software and documentation in the JDK 1.1.3, JFC 1.1, and Swing 1.0.1 download packages. Swing 1.0.1 was contained in JFC 1.1.
The 1.0.1 version of Swing is designed to be compatible with JDK 1.1.
A new version of Swing is scheduled to be incorporated in JDK 1.2.
Hopefully there won't be too many changes and the material in this lesson
will continue to be relevant.
This lesson provides a preview of an alternative to the AWT for creating GUIs - Swing.
Swing provides a set of lightweight components that can be used in place of the components in the AWT, sometimes to advantage. (See other lessons for a discussion of the difference between lightweight and heavyweight components.) In addition, Swing provides a number of components that are not contained in the AWT (progress bars, tool tips, trees, combo boxes, etc.).
At one level, the Swing components can be viewed simply as a replacement for the AWT components. At another level, however, the Swing components are much more sophisticated than the AWT components for a variety of reasons. For example, additional event structures are available with the Swing components and it is possible to change the "look and feel" of the GUI at runtime.
Perhaps even more important, the Swing components are designed using a Model-View-Control (MVC) concept which makes them significantly more powerful. A lesson on MVC is provided later. In brief, MVC is a concept where data is provided by one or more controls, maintained by a model, and displayed by one or more views. MVC is an OOD design pattern that is strongly recommended for the design of robust systems. With MVC, either the control or the view can be modified without the attendant requirement to modify the model.
One important manifestation of MVC in the Swing components is that you can make them look like anything you please. This is referred to as Pluggable Look and Feel (PLAF). Swing 1.0.1 comes with three different PLAF implementations right out of the box:
Therefore, if you create a GUI using exclusively Swing components, you can provide your customer with the ability to switch between L&F implementations at runtime. A sample program will be provided later in this lesson that demonstrates this capability.
When you download Swing 1.0.1, a demonstration program named SwingSet comes with the download that illustrates most of the available Swing components. This program also allows you to switch the L&F at runtime.
If you run that program, you will see another important difference between Swing and the AWT. In particular, most of the Swing components are containers. This means that they can contain other components and can also contain images. Therefore, creation of GUI components containing images (such as button bars with icons) is not too difficult with Swing.
If we were to start from scratch, we could write an entire book on Swing. However, much of the information in that book would duplicate material already provided in discussions of the AWT. Therefore, the plan at this time is to provide a series of lessons that highlight the differences between Swing and the AWT, under the assumption that you already understand the AWT.
Sometimes when you change the L&F of a GUI, you can be surprised
by the result. Therefore, it is important to have the ability to
test your GUI for different L&F implementations. For that reason,
we will begin our investigation into Swing by providing a program that
makes it easy to perform such testing.
To associate this object with a GUI under test, simply pass a reference to the JFrame containing the GUI as a parameter when this object is constructed. Typically, you will instantiate an object of this class and add it to a JFrame object that is separate from the GUI under test. The only link between the two will be that the object of this class has a reference to the GUI under test so that it can change the L&F of that GUI.
This class creates a JPanel. The JPanel contains one JButton for each L&F implementation in the currently installed JDK. Clicking each JButton will cause the L&F of the GUI under test to change to the L&F represented by that JButton. (The L&F of the object of this class also changes as well.)
The name of the L&F is displayed on the JButton.
This class will be used with a variety of programs in this tutorial in order to test them against the currently installed look and feel implementations.
The key statements in selecting and then implementing the new L&F
are:
UIManager.setLookAndFeel(plafClassName);
SwingUtilities.updateComponentTreeUI(thisPlafPanel); SwingUtilities.updateComponentTreeUI(testGui); |
import java.awt.event.*; import com.sun.java.swing.*; import java.util.*; |
The best available information seems to be the following statement that
was extracted from the description of the method named getInstalledLookAndFeels()
that returns an array object of this type (a minor typo was corrected by
the author in this quotation):
"Return an array of objects that provide some information about the
LookAndFeel implementations that have been installed with this java development
kit. The LookAndFeelInfo objects can be used by an application
to construct a menu of look and feel options for the user or to set the
look and feel at start up time."
|
public class PlafPanel01 extends JPanel { UIManager.LookAndFeelInfo[] plafInfoArray; |
Also, in order for the event handler to control the L&F of an object
of this class, it must have a reference to the object. The next fragment
shows two variables that are used to save these two required references.
JFrame testGui;//save a reference to the test GUI here PlafPanel01 thisPlafPanel = this;//ref to this object |
public PlafPanel01(JFrame testGui) {//constructor this.testGui = testGui;//save ref to test GUI this.add(new JLabel(//label the PlafPanel "PL&F Selection Panel, Copyright 1998, RGBaldwin")); |
plafInfoArray = UIManager.getInstalledLookAndFeels(); |
The declaration of the Vector and the beginning of the for
loop with a statement to populate the Vector are shown in the next
code fragment.
Vector theButtons = new Vector(plafInfoArray.length); for(int cnt = 0; cnt < plafInfoArray.length; cnt++){ theButtons.addElement(new JButton()); |
//Still in the for loop String theClassName = plafInfoArray[cnt].getClassName(); |
Note the requirement to downcast the reference extracted from the Vector.
//Still in the for loop String label = theClassName.substring( theClassName.lastIndexOf(".")+1); ((JButton)theButtons.elementAt(cnt)).setText(label); |
Note that because the references to the buttons are stored as type Object
in a Vector, it is necessary to downcast them before they can be
used. The name of the L&F class is also needed by the Action Event
handler, so we pass it as a parameter to the constructor for the listener
object.
((JButton)theButtons.elementAt(cnt)). addActionListener(new MyActionListener( theClassName)); |
//Still in the for loop //Add each JButton to the JPanel. add((JButton)theButtons.elementAt(cnt)); }//end for loop }//end constructor |
The following code fragment shows the constructor for the listener object
which saves the name of the L&F class associated with the button on
which the listener is registered.
class MyActionListener implements ActionListener{ String plafClassName;//save name of plaf class here MyActionListener(String plafClassName){ this.plafClassName = plafClassName; }//end constructor |
public void actionPerformed(ActionEvent e){ //Set the current default L&F to that passed in as // a parameter try{ UIManager.setLookAndFeel(plafClassName); }catch(Exception ex){System.out.println(ex);} |
The following code fragment accomplishes this by invoking the static updateComponentTreeUI() method on two different references:
"A simple minded look and feel change: ask each node in the tree to updateUI(), i.e. to initialize its UI property with the current look and feel. |
//Set the L&F for this PlafPanel01 object SwingUtilities.updateComponentTreeUI(thisPlafPanel); //Set the L&F for the test GUI object SwingUtilities.updateComponentTreeUI(testGui); }//end actionPerformed() }//end class MyActionListener }//end class PlafPanel01 |
A complete listing of this program is contained in the next section.
The program discussed in the sections sections following the program
listing put this class to work by using it to invoke different L&F
implementations on a simple GUI consisting of a JButton and a JTextField.
/*File PlafPanel01 Copyright 1998, R.G.Baldwin The purpose of this class is to construct an object that can easily be associated with a GUI to test the GUI for all of the Look and Feel implementations installed with the current JDK. To associate this object with a GUI under test, pass a reference to the JFrame containing the GUI as a parameter when this object is constructed. Typically, you will instantiate an object of this class and add it to a JFrame object that is separate from the GUI under test. This class creates a JPanel. The JPanel contains one JButton for each L&F implementation in the currently installed JDK. Clicking each JButton will cause the L&F of the GUI to change to the L&F represented by that JButton. The name of the L&F is displayed on the JButton. This class will be used with a variety of programs in this tutorial in order to test them against the currently installed look and feel implementations. The key statements in selecting and then implementing the new L&F are: UIManager.setLookAndFeel(plafClassName); SwingUtilities.updateComponentTreeUI(thisPlafPanel); SwingUtilities.updateComponentTreeUI(testGui); These statements are discussed further in the comments in the program. This class was tested using JDK 1.1.3 and Swing 1.0.1 under Win95. **********************************************************/ import java.awt.event.*; import com.sun.java.swing.*; import java.util.*; public class PlafPanel01 extends JPanel { /* The following is an unusual reference variable type that is used to refer to an array of L&F information. As of Swing 1.0.1, the document that should explain this type is missing from the download documentation file. The best available information seems to be the following statement that was extracted from the description of the method named getInstalledLookAndFeels() that returns an array object of this type (a minor typo was corrected by the author in this quotation): "Return an array of objects that provide some information about the LookAndFeel implementations that have been installed with this java development kit. The LookAndFeelInfo objects can be used by an application to construct a menu of look and feel options for the user or to set the look and feel at start up time." */ UIManager.LookAndFeelInfo[] plafInfoArray; //-----------------------------------------------------// JFrame testGui;//save a reference to the test GUI here PlafPanel01 thisPlafPanel = this;//ref to this object public PlafPanel01(JFrame testGui) {//constructor this.testGui = testGui;//save ref to test GUI this.add(new JLabel(//label the PlafPanel "PL&F Selection Panel, Copyright 1998, RGBaldwin")); //Get the list of L&Fs installed with the current JDK //See note above regarding this method. plafInfoArray = UIManager.getInstalledLookAndFeels(); //Create a vector of references to JButton objects // with one element in the vector for each L&F // implementation in the current JDK. Vector theButtons = new Vector(plafInfoArray.length); //Create one JButton object for each L&F implementation // and put its reference in the Vector. for(int cnt = 0; cnt < plafInfoArray.length; cnt++){ theButtons.addElement(new JButton()); //Get the name of the class for each specific L&F // implementation String theClassName = plafInfoArray[cnt].getClassName(); //Extract a short name for each specific L&F // implementation and use it to label the JButton // corresponding to that L&F. The short name appears // following the last period in the String // representation of the class name for the // L&F implementation. Note the requirement to // downcast the reference extracted from the Vector. String label = theClassName.substring( theClassName.lastIndexOf(".")+1); ((JButton)theButtons.elementAt(cnt)).setText(label); //Add an action listener to each JButton that will // cause the L&F to change to the one represented by // that JButton whenever the JButton is clicked. Note // that because the references to the buttons are // stored in a Vector, it is necessary to downcast // them from Object to JButton. ((JButton)theButtons.elementAt(cnt)). addActionListener(new MyActionListener( theClassName)); //Add each JButton to the JPanel. add((JButton)theButtons.elementAt(cnt)); }//end for loop }//end constructor //=====================================================// //Inner class for action listeners class MyActionListener implements ActionListener{ String plafClassName;//save name of plaf class here //Constructor MyActionListener(String plafClassName){ //save the incoming parameter this.plafClassName = plafClassName; }//end constructor public void actionPerformed(ActionEvent e){ //Set the current default L&F to that passed in as // a parameter try{ UIManager.setLookAndFeel(plafClassName); }catch(Exception ex){System.out.println(ex);} //Now implement the current default L&F to make it // take effect. The description of the // updateComponentTreeUI() method as extracted from // the documentation is as follows: //"A simple minded look and feel change: ask each // node in the tree to updateUI(), i.e. to // initialize its UI property with the current look // and feel." //Set the L&F for this PlafPanel01 object SwingUtilities.updateComponentTreeUI(thisPlafPanel); //Set the L&F for the test GUI object SwingUtilities.updateComponentTreeUI(testGui); }//end actionPerformed() }//end class MyActionListener }//end class PlafPanel01 //=======================================================// |
It also illustrates a couple of requirements of using Swing components that differ from using AWT components.
The program creates a GUI consisting of a PlafPanel01 object in a JFrame which is used to control the L&F of the program. The PlafPanel01 object is instantiated from the custom class named PlafPanel01 and contains one JButton for each L&F implementation in the currently installed JDK.
The name of the L&F is displayed on each JButton on the PlafPanel.
The PlafPanel01 is added to the content pane of a JFrame. Clicking each JButton on the PlafPanel01 will cause the L&F of the test GUI and the L&F of the PlafPanel01 to change to the L&F represented by that JButton.
The program also creates another GUI in a JFrame which is intended to
simulate a GUI under test. This GUI contains an inactive JButton
and an inactive JTextField. These components are provided so that
you can see how they behave when the L&F is changed.
This program was tested using JDK 1.1.3 and Swing 1.0.1 under Windows
95
If you compile and run this program, you should see two JFrame objects on your screen. The one at the upper left is the GUI under test and contains an inactive JButton object and an inactive JTextField object. They were placed there so that you can see how they look and behave when you change among the available L&F implementations on your system.
The JFrame object below that one should contain one JButton object for
each of the L&F implementations in your currently installed JDK.
(If your system contains a large number of L&F implementations, some
of the buttons may not be visible and you may need to stretch the JFrame
manually.) Each of these buttons should be labeled with the name of the
L&F to which it applies. Clicking on these buttons should cause
the L&F of both JFrame objects to change to the specific L&F identified
by the button.
About the only thing that is new in the following code is the requirement
to use the getContentPane() method for adding components to the
JFrame object. This is one of the major changes between an AWT Frame
object and a Swing JFrame object. A JFrame object is composed of
different layers or panes which will be discussed in a subsequent lesson
and you must add new components to the content pane..
public class Swing04 extends JFrame {//controlling class Swing04(String title) {//constructor // Set the title of the JFrame super(title); //Now create the test GUI for this application. getContentPane().setLayout(new FlowLayout()); //Add some components to be viewed with the new L&F getContentPane().add(new JButton("Dummy JButton")); getContentPane().add(new JTextField("Dummy JTextField")); }//end constructor |
The main method also uses an anonymous inner class to create a Window Listener object for terminating the program when the user presses the close button on the JFrame.
The following code fragment shows the instantiation of the test GUI
object and the instantiation of the Window Listener object. There
is really nothing new in this code fragment relative to AWT code that we
have seen before.
public static void main(String args[]) { JFrame jFrame = new Swing04( "Pluggable L&F, Copyright 1998, RG Baldwin"); // Set the size of the JFrame and show it jFrame.setSize(400,100); jFrame.setVisible(true); // Create a WindowAdapter to terminate the program // when the window is closed. jFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); }//end windowClosing() });//end WindowListener |
It is important to note that a reference to the test GUI object (jFrame) is passed to the constructor of the PlafPanel01 object when it is instantiated. It is this reference that makes it possible for the event handlers in the object to control the L&F of the test GUI object.
Other than the required use of getContentPane(), there is nothing
in this code fragment that we haven't seen many times in our work with
the AWT.
//Still in main() JFrame myPlafPanel = new JFrame( "PL&F Panel,Copyright 1998,RG Baldwin"); myPlafPanel.getContentPane().add( new PlafPanel01(jFrame)); myPlafPanel.setBounds(0,400,400,150); myPlafPanel.setVisible(true); }//end main() }//end class Swing04 |
/*File Swing04 Copyright 1998, R.G.Baldwin The primary purpose of this program is to demonstrate pluggable look and feel (PL&F) and the ability to change the L&F at runtime. It also illustrates a couple of requirements of using Swing components that differ from using AWT components. The program creates a GUI in a JFrame containing a PlafPanel. The PlafPanel01 is instantiated from a custom class named PlafPanel01 and contains one JButton for each L&F implementation in the currently installed JDK. The name of the L&F is displayed on each JButton on the PlafPanel01. The PlafPanel01 is added to the content pane of the JFrame. Clicking each JButton on the PlafPanel01 will cause the L&F of the GUI to change to the L&F represented by that JButton. The program also creates another GUI in a JFrame which is intended to simulate a GUI under test. This GUI contains an inactive JButton and an inactive JTextField. These components are provided so that you can see how then behave when the L&F is changed. It is instructive to see how shapes and colors for these inactive components change as each different L&F is implemented. Tested using JDK 1.1.3 and Swing 1.0.1 under Windows 95 *********************************************************/ import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; import java.util.*; // Subclass JFrame to display a window public class Swing04 extends JFrame {//controlling class Swing04(String title) {//constructor // Set the title of the JFrame super(title); //Now create the test GUI for this application. getContentPane().setLayout(new FlowLayout()); //Add some components to be viewed with the new L&F getContentPane().add(new JButton("Dummy JButton")); getContentPane().add(new JTextField("Dummy JTextField")); }//end constructor //-----------------------------------------------------// // Create main method to execute the application public static void main(String args[]) { JFrame jFrame = new Swing04( "Pluggable L&F, Copyright 1998, RG Baldwin"); // Set the size of the JFrame and show it jFrame.setSize(400,100); jFrame.setVisible(true); // Create a WindowAdapter to terminate the program // when the window is closed. jFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); }//end windowClosing() });//end WindowListener //Create a PlafPanel in a floating JFrame that can be // used to test this GUI. JFrame myPlafPanel = new JFrame( "PL&F Panel,Copyright 1998,RG Baldwin"); myPlafPanel.getContentPane().add( new PlafPanel01(jFrame)); myPlafPanel.setBounds(0,400,400,150); myPlafPanel.setVisible(true); }//end main() }//end class Swing04 //=======================================================// |
-end-