Java Programming, Lecture Notes # 124, Revised 08/26/98.
What is a hidden button? If you use Netscape Communicator 4.x, then you are familiar with the type of buttons that I refer to as hidden buttons.
For example, the "Back" button in the Netscape browser in Communicator 4.x is a button of this type. Its normal appearance is to display the word Back and a little green icon but there are no borders to cause it to look like a button. However, when you touch it with the mouse, it suddenly displays borders that make it look like a button. The new capability of Swing to deal with borders makes it possible to create buttons that behave like this in Java.
Swing also makes it possible to place images on buttons (or on just about any Swing component for that matter). We will use ImageIcon to create images to place on our buttons.
Not only will we have icons on the buttons, they will change as we move the mouse in the vicinity of the buttons. This is the result of a new capability in Swing referred to as rollover effects.
You have all seen tool tips in various applications. Swing supports tool tips and we will demonstrate that capability in this lesson as well.
As you are already aware from a previous lesson, Swing supports Pluggable
Look and Feel (PL&F). Also, as you are already aware from
a previous lesson, Swing makes it possible for components to contain other
components. Just for fun, we will build a pyramid of nested JButton
components and observe them for different PL&F implementations.
By coming alive, they take on the appearance of a button according to the current look and feel. This behavior is implemented using the new border capability of Swing. Also, the icon on the button changes to a different image when you touch the button with the mouse. This illustrates the new ImageIcon capability of Swing and several related behaviors (such as rollover effects).
A PL&F control panel is provided so that you can change the L&F during runtime. Thus, the program also illustrates the appearance of Hidden Buttons under different look and feel implementations.
As mentioned earlier, the program also illustrates the use of tooltips.
And just for fun, the program illustrates that every Swing component is a container that can contain other components. This capability is illustrated by building a pyramid of nested JButton objects where each object contains the objects above it.
It is very interesting to observe the appearance of this pyramid under different L&F implementations. For example, under the JavaSoft Metal L&F, it doesn't have a three-dimensional pyramid look. However, under the Windows and Motif L&F, the three-dimensional appearance is very pronounced.
This program requires that several gif image files be placed in the directory that contains the class files. Otherwise, you won't be able to compile and run the program.
It doesn't matter what images the gif files contain. Of course, to fit in the allotted space, they should be small images. To be directly compatible with this program, the names of the gif files must be bulb1, bulb2, and bulb3. Obviously, you could modify the program to accommodate gif files with other names as well.
These gif files are used to change the icons on the buttons to illustrate the different states of the buttons.
When the program starts, two Hidden Buttons and a pyramid of nested JButton objects are displayed in a JFrame object on the screen.
In addition, a separate JFrame object is displayed containing buttons that can be used to select any of the different L&F implementations currently installed on the system. When you select an L&F implementation on this JFrame, it is applied to all of the components on both JFrame objects.
One of the Hidden Buttons is a JButton. The other is a JToggleButton. They are identified by the text on the buttons as well as the tool tip that appears when you cause the mouse pointer to hover over a button for a short period of time.
In my implementation, using my three gif files, the normal state of the buttons is to display a light bulb that is not illuminated.
When you touch the button with the mouse (rollover), the borders appear and the light bulb image changes to one that is illuminated.
When you press the button, the light bulb changes to one that is blue and it remains blue until you release the mouse button. (See a later comment regarding the different rollover behavior of the JToggleButton when it is selected.)
Of course, your version will be somewhat different, depending on the three images that you select.
The buttons don't do anything when you click them because no action listeners were registered on them.
This program illustrates the JToggleButton which is new to Swing.
Whereas a normal JButton object responds and returns to its original appearance when you click it, a JToggleButton responds but does not return to its original appearance. In other words, clicking on a JToggleButton causes it to toggle between two different appearances (and two different readable states as well).
Although it isn't demonstrated in this program, the two states of a JToggleButton are true and false. Your program can determine the state by invoking the isSelected() method on the button. The change in appearance between being selected and not selected depends on the L&F in effect at the time.
Note that unlike the JButton object, the JToggleButton object doesn't change to the rolloverIcon when it is in the selected state. It displays the normalIcon instead. However, like the JButton, it does display the rolloverIcon when it is touched by the mouse pointer but is not selected.
This program was tested using JDK 1.1.6 and Swing 1.0.1 under Windows
95
The main() method instantiates two objects. The first is an object of the type of the controlling class. The second is an object of the class named PlafPanel02.
You should already be familiar with the concept involved with this second object. It was explained in an earlier lesson on Pluggable Look & Feel. This version of the PlafPanel class differs from the one used in that lesson only to the extent that this is a totally stand-alone control panel for selecting the L&F of the associated GUI. A complete listing of the source code for PlafPanel02 is provided at the end of this lesson.
A GUI is associated with this L&F control panel by passing a reference to the GUI when the PlafPanel02 object is instantiated. From that point forward, selecting a new L&F on the control panel will cause the L&F of the associated GUI to change to the selected L&F (the L&F of the control panel also changes as well).
The GUI named demoFrame is passed to the constructor for PlafPanel02
to associate the GUI with the L&F control panel in this program.
public class Swing09 extends JFrame { public static void main(String args[]) { Swing09 demoFrame = new Swing09(); PlafPanel02 plafPanel = new PlafPanel02(demoFrame); }//end main() |
That brings us to the constructor for the primary GUI for this program.
We begin with the preliminaries by creating a title, setting the layout,
and adding a JLabel object that contains some explanatory information.
Swing09() {//constructor this.setTitle("Copyright 1998, RG Baldwin"); this.getContentPane().setLayout(new FlowLayout()); this.getContentPane().add(new JLabel( "Hidden Button Demo, Copyright 1998, R.G.Baldwin")); |
The next fragment instantiates a JPanel object containing
the Hidden Buttons and adds the panel to the JFrame object that
is the GUI. We will examine the class from which is panel is constructed
shortly.
this.getContentPane().add(new HiddenButtonPanel()); |
Next, just for fun, we build a pyramid of nested JButton objects so that we can observe their appearance under the different L&F implementations and set a tool tip on the top button..
Following this, we set the size of the GUI and make it visible.
Then we add an anonymous inner-class WindowListener to terminate the program when the user closes the JFrame object.
And that ends the constructor for the controlling class and also ends
the controlling class definition.
JButton a = new JButton(); JButton b = new JButton(); JButton c = new JButton(); JButton d = new JButton(); JButton e = new JButton(); JButton f = new JButton("Top"); f.setToolTipText("Nested JButton objects"); this.getContentPane().add(a); a.add(b); b.add(c); c.add(d); d.add(e); e.add(f); //Set size and display the GUI this.setSize(400,280); this.setVisible(true); // Create an inner class WindowAdapter to terminate the // program when the JFrame is closed. this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0);}});//end WindowListener }//end constructor }//end class Swing09 |
That brings us to the definition of the class that is used to instantiate a panel containing the two Hidden Buttons.
We begin by instantiating three different objects of type ImageIcon. The ImageIcon class implements the Icon interface, so these objects can be used anywhere an object of type Icon is required.
In our case, we will need to provide a reference to an object of type Icon in three different statements associated with each of our Hidden Buttons. We will discuss those statements later.
There are several different constructors that can be used to instantiate an object of type ImageIcon. We are using a version of the constructor that requires a String specifying the name of the file containing the image.
This fragment also declares reference variables for our two Hidden Button
objects.
class HiddenButtonPanel extends JPanel{ ImageIcon normalIcon = new ImageIcon("bulb3.gif"); ImageIcon rolloverIcon = new ImageIcon("bulb2.gif"); ImageIcon pressedIcon = new ImageIcon("bulb1.gif"); JToggleButton myJToggleButton; JButton myJButton; |
That brings us to the constructor for our HiddenButtonPanel class. There is a lot of new material contained in this constructor.
This constructor instantiates two different Hidden Buttons;
We begin by instantiating a JButton object using a constructor that allows us to specify a reference to an Icon image object that will appear on the button. The ability to put images on a button is new to Swing. This capability does not exist in the AWT. In this case, we specify one of the ImageIcon objects instantiated earlier (normalIcon).
Next, we invoke the setRolloverIcon() method to specify an image that will automatically appear on the button when we touch the button with the mouse pointer. Again, we provide a reference to one of the ImageIcon objects instantiated earlier (rolloverIcon),.
After this, we invoke the setPressedIcon() method to specify an image that will automatically appear on the button while it is pressed, passing one of our ImageIcon objects as a parameter (pressedIcon).
These are all new capabilities in Swing that do not exist in the AWT.
Finally, as the last action in this fragment, we invoke the setRolloverEnabled() method on the button to "enable the rollover effects."
As mentioned earlier, I was unable to find any explanatory information
in the documentation for Swing 1.0.1 regarding the "rollover effects.".
Everything here was inferred from the very cryptic statements in the API
documentation and an examination of the example programs that came with
the Swing download.
HiddenButtonPanel(){//constructor myJButton = new JButton("JButton", normalIcon); myJButton.setRolloverIcon(rolloverIcon); myJButton.setPressedIcon(pressedIcon); myJButton.setRolloverEnabled(true); |
Tool Tips are amazingly easy to create with Swing. The first statement in the next fragment is all that is required to associate a tool tip with an object.
After setting the text for the tool tip on the button by invoking the setToolTipText() method, we cause the borders on the button to be invisible by invoking setBorderPainted() and passing false as a parameter.
Until this is changed, the borders on the button will not be painted, and the button really won't be recognizable as a button.
We wrap up this fragment by adding the JButton object to the
panel.
myJButton.setToolTipText("This is a JButton"); myJButton.setBorderPainted(false); this.add(myJButton); |
We will wrap up this discussion with the code fragment that causes the button to appear when you touch it with the mouse pointer and causes it to disappear when you move the mouse pointer away.
This is a simple anonymous inner-class mouse listener that responds when the mouse enters and leaves the boundary of the button. Except for the fact that it invokes the setBorderPainted() method with either true or false to expose the borders of the button or make them invisible, there is nothing new here. You should be completely familiar with this approach to processing mouse events.
Note that in this program, we processed mouse events to cause the borders on the button to be either visible or invisible.
It is also very important to note that we weren't required to provide any special event handling to cause the icon on the button to change when the mouse pointer entered and exited the boundary of the button, and to change again when the button was pressed. That behavior is automatic once you properly invoke the setRolloverIcon(), setPressedIcon(), and setRolloverEnabled() methods discussed earlier.
In a way, I hate to see this because it tends to break the structured
nature of the API. Normally it is necessary for us to provide event
handlers to deal with these kinds of events, but in this case, the
event handling is being provided automatically. If much of this sort
of thing creeps into the API, it could lead to confusion as to when
it is and when it is not necessary to provide event handling.
myJButton.addMouseListener(new MouseAdapter(){ public void mouseEntered(MouseEvent mouseEvent){ myJButton.setBorderPainted(true); }//end mouseEntered public void mouseExited(MouseEvent mouseEvent){ myJButton.setBorderPainted(false); }//end mouseExited() });//end inner-class definition |
.
/*File Swing09 Copyright 1998, R.G.Baldwin The primary purpose of this program is to demonstrate "Hidden Buttons". These are buttons which don't look like buttons until you point to them with the mouse but which "come alive" when you point to them. By coming alive, they take on the appearance of a button according to the current look and feel. This behavior is implemented using the new border capability of Swing. Also, the image icon on the button changes to a different image when you point to the button with the mouse. This illustrates the new image icon capability of Swing. The program illustrates the appearance of Hidden Buttons under different look and feel implementations. The program also illustrates the use of tooltips. And just for fun, the program also illustrates that every Swing component is a container that can contain other components. This is done by building a pyramid of nested JButton objects where each contains the objects above it. It is interesting to observe the different appearance of this pyramid under different L&F implementations. This program requires that several icon sized gif files be placed in the directory that contains the class files. The names of the gif files must be bulb1, bulb2, and bulb3. These gif files are used to illustrate the different states of image icons. When the program starts, two Hidden Buttons and a pyramid of buttons are displayed in a JFrame. In addition, a separate JFrame is displayed containing buttons that can be used to select any of the different L&F implementations currently installed on the system. When you select a L&F implementation on this JFrame, it is applied to all of the components on both JFrame objects. One of the Hidden Buttons is a JButton. The other is a JToggleButton. They are identified by the text on the button as well as the tool tip that appears when you pause with the mouse pointing at one of the buttons. The buttons don't do anything when you click them because no action listeners were registered on them. Note that the JToggleButton doesn't respond to the rolloverIcon when it is in the pressed state. It displays the normalIcon instead. Tested using JDK 1.1.6 and Swing 1.0.1 under Windows 95 **********************************************************/ import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; // Subclass JFrame for the GUI public class Swing09 extends JFrame {//controlling class public static void main(String args[]) { //Instantiate an object of this type Swing09 demoFrame = new Swing09(); //Instantiate and link a PlafPanel to it PlafPanel02 plafPanel = new PlafPanel02(demoFrame); }//end main() //-----------------------------------------------------// Swing09() {//constructor this.setTitle("Copyright 1998, RG Baldwin"); this.getContentPane().setLayout(new FlowLayout()); this.getContentPane().add(new JLabel( "Hidden Button Demo, Copyright 1998, R.G.Baldwin")); //Instantiate and add the panel containing the hidden // buttons. this.getContentPane().add(new HiddenButtonPanel()); //Just for fun, build a pyramid of nested JButton // objects and observe their appearance under the // different L&F implementations. JButton a = new JButton(); JButton b = new JButton(); JButton c = new JButton(); JButton d = new JButton(); JButton e = new JButton(); JButton f = new JButton("Top"); f.setToolTipText("Nested JButton objects"); this.getContentPane().add(a); a.add(b); b.add(c); c.add(d); d.add(e); e.add(f); //Set size and display the GUI this.setSize(400,280); this.setVisible(true); // Create an inner class WindowAdapter to terminate the // program when the JFrame is closed. this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0);}});//end WindowListener }//end constructor //-----------------------------------------------------// }//end class Swing09 //=======================================================// //This class is used to instantiate a panel containing // two Hidden Buttons. class HiddenButtonPanel extends JPanel{ //Create new image icons using gif files. You will need // to provide icon sized gif files with names that match // the file names in the following statements to be able // to run this program. These images will appear on the // buttons in their different states. ImageIcon // implements the Icon interface so it can be used // wherever an Icon is required (such as instantiation // of a JButton with an Icon). ImageIcon normalIcon = new ImageIcon("bulb3.gif"); ImageIcon rolloverIcon = new ImageIcon("bulb2.gif"); ImageIcon pressedIcon = new ImageIcon("bulb1.gif"); //Declare references to two buttons of two different // types. JToggleButton myJToggleButton; JButton myJButton; HiddenButtonPanel(){//constructor //Create and prep the JButton object //Instantiate a JButton object with a caption and // an icon. myJButton = new JButton("JButton", normalIcon); //Establish icon to be displayed during mouse rollover myJButton.setRolloverIcon(rolloverIcon); //Establish icon to be displayed when button is pressed myJButton.setPressedIcon(pressedIcon); //Enable the Swing rollover effects. myJButton.setRolloverEnabled(true); //Set text for the tool tip for the button. myJButton.setToolTipText("This is a JButton"); //Hide the border on the button myJButton.setBorderPainted(false); //Add the button to the panel this.add(myJButton); //Inner class listener to display and hide borders // on the button when the mouse enters and exits. myJButton.addMouseListener(new MouseAdapter(){ public void mouseEntered(MouseEvent mouseEvent){ //Display border on the button myJButton.setBorderPainted(true); }//end mouseEntered public void mouseExited(MouseEvent mouseEvent){ //Hide the border on the button myJButton.setBorderPainted(false); }//end mouseExited() });//end inner-class definition //Create and prep the JToggleButton object . See // explanatory comments in the code for the JButton //above. myJToggleButton = new JToggleButton( "JToggleButton", normalIcon); myJToggleButton.setRolloverIcon(rolloverIcon); myJToggleButton.setPressedIcon(pressedIcon); myJToggleButton.setRolloverEnabled(true); myJToggleButton.setToolTipText( "This is a JToggleButton"); myJToggleButton.setBorderPainted(false); this.add(myJToggleButton); //Inner class listener to respond to rollover myJToggleButton.addMouseListener(new MouseAdapter(){ public void mouseEntered(MouseEvent mouseEvent){ myJToggleButton.setBorderPainted(true); }//end mouseEntered public void mouseExited(MouseEvent mouseEvent){ myJToggleButton.setBorderPainted(false); }//end mouseExited() });//end inner-class definition }//end constructor }//end class HiddenButtonPanel //=======================================================// |
/*File PlafPanel02 Copyright 1998, R.G.Baldwin This is a revision to PlafPanel01. This version is designed to stand alone inside its own JFrame object. To use this program, simply instantiate an object of this type and pass a reference to the JFrame under test as a parameter. 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. This class creates a JFrame. The JFrame 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. 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. Tested using JDK 1.1.6 and Swing 1.0.1 under Win95. **********************************************************/ import java.awt.event.*; import java.awt.*; import com.sun.java.swing.*; import java.util.*; public class PlafPanel02 extends JFrame { /* 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 PlafPanel02 thisPlafPanel = this;//ref to this object public PlafPanel02(JFrame testGui) {//constructor this.testGui = testGui;//save ref to test GUI this.getContentPane().add(new JLabel( "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 JFrame. this.getContentPane().add((JButton)theButtons. elementAt(cnt)); }//end for loop this.getContentPane().setLayout(new FlowLayout()); this.setTitle("Copyright 1998, R.G.Baldwin"); this.setBounds(30,300,350,150); this.setVisible(true); }//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 PlafPanel object SwingUtilities.updateComponentTreeUI(thisPlafPanel); //Set the L&F for the test GUI object SwingUtilities.updateComponentTreeUI(testGui); }//end actionPerformed() }//end class MyActionListener //main method for stand-alone testing public static void main(String[] args){ new PlafPanel02(new JFrame()); }//end main() }//end class PlafPanel02 //=======================================================// |