Understanding Action Objects in Java
getKeys method
Returns an array of Object references which are the keys for which values have been set. Returns null if no keys have their values set. Can be used in an action-aware component to identify and get the values for keyed properties.
getPropertyChangeListeners()
Returns an array of all the PropertyChangeListeners added to this AbstractAction object with addPropertyChangeListener(). The array is empty if no listeners have been added. Can be used in a subclass to get a list of listeners.
Now for some code
As is my practice, I will discuss the program named ActionObj02 in
fragments. A complete listing of the program is shown in Listing
22 near the end of the lesson. The first fragment, shown in Listing
1, simply uses the main method to instantiate an object of the GUI
class. I will discuss the definition of the GUI class later.
public class ActionObj02{ public static void main( String[] args){ new GUI(); }//end main }//end ActionObj02 Listing 1 |
The MyAction class
Listing 2 shows the entire class definition for the MyAction
class from which Action objects will be instantiated.
class MyAction extends AbstractAction{ public void actionPerformed( ActionEvent e){ System.out.println("Action: " + ((AbstractButton)e.getSource()). getActionCommand()); }//end actionPerformed }//end class MyAction Listing 2 |
Note that the MyAction class doesn't implement the Action interface directly. Rather, it extends the AbstractAction class and implements the Action interface through inheritance. This causes it to inherit the default behavior of the methods defined in the AbstractAction class and discussed earlier in this lesson.
Overrides actionPerformed
The MyAction class overrides the actionPerformed method. The overridden behavior of the method is to get and display the action command for any source object (on which it is registered) that fires an ActionEvent. By default, the action command is simply the text that is visible on the face of the source object. Thus, clicking any of the six components located on the JMenu, the JToolBar, or the JPanel (see Figure 4) will cause the text on the face of that component to be displayed.
The GUI class
Listing 3 shows the beginning of the GUI class.
class GUI extends JFrame{ JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("Menu"); JToolBar toolBar = new JToolBar(); JPanel panel = new JPanel(); Listing 3 |
The code in Listing 3 creates the following container objects, which are visible in Figure 4:
- JMenu
- JToolBar
- JPanel
Listing 4 creates two Action objects (instances of the MyAction
class discussed above). These two Action objects will
be registered on various action-aware components later.
Action actionObj01 = new MyAction(); Action actionObj02 = new MyAction(); Listing 4 |
Control
Listing 5 creates the four check boxes and a control panel shown in
the center of Figure 3.
JCheckBox ckBox01 = new JCheckBox( "Disable01"); JCheckBox ckBox02 = new JCheckBox( "Disable02"); JCheckBox ckBox03 = new JCheckBox( "Toggle Icon01"); JCheckBox ckBox04 = new JCheckBox( "Toggle Icon02"); JPanel controlPanel = new JPanel(); Listing 5 |
The four check boxes will be used to manipulate the behavior of the two Action objects, and hence the behavior of the six associated action-aware components. Note that the text shown on the face of the checkboxes will be modified when those checkboxes are checked and cleared.
The GUI constructor
Listing 6 shows the beginning of the constructor for the GUI
class.
GUI(){//constructor menuBar.add(menu); menuBar.setBorder( new BevelBorder( BevelBorder.RAISED)); setJMenuBar(menuBar); Listing 6 |
The code in Listing 6 adds the JMenu object to the JMenuBar object, which is the standard way to construct a menu in Java.
Then it invokes the setBorder method on the menu bar to cause its edges to become visible. I did this to make it easy to visually separate the menu bar from the tool bar that appears immediately below it in Figure 3. (You can learn more about Swing borders at Baldwin's Java Programming Tutorials.)
Set the JMenuBar
Finally, the code in Listing 6 adds the menu bar to the JFrame. Note, however, that this is accomplished by invoking the setJMenuBar method on the JFrame object instead of invoking an add method, (which is the way that other components are normally added to a JFrame container.)
A decorated JToolBarcontainer
Listing 7 decorates the JToolBar object with raised bevel borders,
and places it in the NORTH position of the JFrame object.
This causes the toolbar to appear immediately below the menu bar in Figure
3.
toolBar.setBorder(new BevelBorder( BevelBorder.RAISED)); getContentPane().add( toolBar,BorderLayout.NORTH); Listing 7 |
(If need be, you can also learn more about the BorderLayout manager and the getContentPane method at Baldwin's Java Programming Tutorials.)
A decorated JPanel container
Listing 8 decorates the JPanel object with raised bevel borders,
and places it in the SOUTH position of the JFrame object.
This causes it to appear at the bottom of the JFrame in Figure 3.
panel.setBorder(new BevelBorder( BevelBorder.RAISED)); getContentPane().add( panel,BorderLayout.SOUTH); Listing 8 |
Setting keyed values
Except for the definition of the MyAction class discussed earlier,
very little of the code discussed so far has been specific to the Action
interface. However, the code in Listing 9 is very specific to the
Action
interface.
actionObj01.putValue( Action.NAME,"actionObj01"); Listing 9 |
Listing 9 invokes the putValue method on one of the Action objects to set the value for one of the keyed properties. As mentioned earlier, each Action object has a container for keyed properties that behaves much as a hashtable behaves. Values are stored against keys, and can be accessed later by specifying the key.
Values are stored in that container by invoking the putValue method and passing a key and a value as parameters. In this case, one of the constants (final variables) defined in the Action interface is used as the key. The literal String object "actionObj01" is passed as the value.
(Note that as discussed earlier, the constant is referred to symbolically as Action.NAME, and no explicit use is made of the actual String value encapsulated in that constant.)The remaining keyed properties
Listing 10 sets the values for each of the remaining keyed properties.
actionObj01.putValue( Action.SMALL_ICON,new ImageIcon( "redball.gif")); actionObj01.putValue( Action.SHORT_DESCRIPTION, "ToolTip for actionObj01"); actionObj02.putValue( Action.NAME,"actionObj02"); actionObj02.putValue( Action.SMALL_ICON,new ImageIcon( "bulb2.gif")); actionObj02.putValue( Action.SHORT_DESCRIPTION, "ToolTip for actionObj02"); Listing 10 |
As mentioned earlier, these keyed properties behave as bound properties, meaning that registered objects are notified when their values change. Checking and clearing two of the checkboxes in this program causes the values of the SMALL_ICON properties to change, thus causing the icons displayed on the components registered on the respective Action objects to change accordingly.
Build the menu
Code that I discussed earlier created a menu bar and placed a menu on
that bar. Now the time has come to place some menu items on that
menu. This process begins in Listing 11.
JMenuItem mnuA1 = new JMenuItem(); mnuA1.setAction(actionObj01); menu.add(mnuA1); Listing 11 |
Invoke the setAction method
The code in Listing 11 is very significant relative to the use of Action objects. Listing 11 instantiates a new JMenuItem object and then invokes the setAction method on that object, passing a reference to one of the Action objects as a parameter.
As discussed earlier, this causes the Action object to be registered as an action listener on the JMenuItem object.
PropertyChange listener registration
More importantly, however, for the purpose of this discussion, invocation of the setAction method causes the JMenuItem object to be registered as a PropertyChange listener on the Action object. Following this, when the enabled property, or any of the keyed properties of the Action object change, the JMenuItem object will be notified of the change.
The JMenuItem class is designed such that this will cause some of the corresponding properties on the JMenuItem object to change in order to track the change on the Action object. (A JMenuItem object doesn't set or track all of the indexed properties.)
After the JMenuItem object has been properly prepared, it is added to the menu in Listing 11.
An alternative to invoking setAction
Listing 12 shown an alternative approach to invoking the setAction
method on the new JMenuItem object. (This approach appears
to achieve the same result as the invocation of the setAction method.).
//Put a JMenuItem on the menu. Set // its Action object in the // constructor. This is a // different approach. JMenuItem mnuA2 = new JMenuItem(actionObj02); menu.add(mnuA2); Listing 12 |
The JMenuItem class has a constructor that accepts a reference to an Action object as a parameter, and according to the Sun documentation, "Creates a menu item whose properties are taken from the specified Action."
An understatement
It appears that the statement in the Sun documentation is something of an understatement. In particular, it appears that constructing the JMenuItem object in this manner causes the action property of that object to be set to the Action object, just as though the setAction method were invoked on that object as in Listing 11.
As explained in the discussion of Listing 11, this involves much more than the Sun documentation statement would imply. In particular, the Action object becomes an action listener on the JMenuItem object, and the JMenuItem also becomes a change listener on the Action object.
Registration of two action listeners
Registering an Action object on an action-aware component
does not prohibit the registration of ordinary ActionListener objects
on the same component. This is illustrated in Listing 13.
JButton butB1 = new JButton(); butB1.addActionListener( new ActionListener(){ public void actionPerformed( ActionEvent e){ System.out.println( "Ordinary Action Listener"); }//end actionPerformed() }//end ActionListener );//end addActionListener butB1.setAction(actionObj01); toolBar.add(butB1); Listing 13 |
Listing 13 instantiates a new JButton object, and then uses an anonymous inner class to register an ordinary ActionListener object on that component.
(If you are unfamiliar with anonymous inner classes, see Baldwin's Java Programming Tutorials.)Then it invokes the setAction method on the same JButton object, which causes the specified Action object to also be registered as an action listener on the JButton object.
Two actionPerformed methods are invoked
Thereafter, when the JButton object is clicked with the mouse, causing an action event to be fired by the JButton object, the following two lines of text appear on the screen:
Action: actionObj01
Ordinary Action Listener
The first line of output text is produced by the actionPerformed method defined in the Action object (see Listing 2).
The second line of output text is produced by the actionPerformed method defined for the ordinary ActionListener object (see Listing 13).
Finish populating the toolbar
Listing 14 finishes populating the toolbar by placing another JButton
object on the toolbar.
JButton butB2 = new JButton(); butB2.setAction(actionObj02); toolBar.add(butB2); Listing 14 |
Page 4 of 6
This article was originally published on May 29, 2002