Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing

Actions are a popular addition to Swing. An action allows a programmer to bundle a commonly used procedure and its bound properties (such as its name and an image to represent it) into a single class. This construct comes in handy if an application needs to call upon a particular function from multiple sources. For example, let's say that a Swing programmer creates an action that saves data to disk. The application could then invoke this action from both the Save menu item on the File menu and the Save button on a toolbar. Both components reference the same action object, which saves the data. If the Save function is disabled for some reason, this property can be set in the action as well. The menu and toolbar objects are automatically notified that they can no longer save any data, and they can relay that information to the user.

3.1.1 Actions and Containers

Swing containers, such as JMenu, JPopupMenu, and JToolBar, can each accept action objects with their add( ) methods. When an action is added, these containers automatically create a GUI component, which the add( ) method then returns to you for customization. For example, a JMenu or a JPopupMenu creates and returns a JMenuItem from an Action while a JToolBar creates and returns a JButton. The action is then paired with the newly created GUI component in two ways: the GUI component registers as a PropertyChangeListener for any property changes that might occur in the action object, while the action object registers as an ActionListener on the GUI component. Figure 3-1 shows the interactions between a menu item or toolbar and an Action.

Figure 3-1. An action in conjunction with a Swing item and toolbar

Essentially, this means that if the menu item or button is selected by the user, the functionality inside the action is invoked. On the other hand, if the action is disabled, it sends a PropertyChangeEvent to both the menu item and the toolbar, causing them to disable and turn gray. Similarly, if the action's icon or name is changed, the menu and toolbar are automatically updated.

3.1.2 The Action Interface

An action is defined by the interface it implements, in this case javax.swing.Action. Action extends the ActionListener interface from AWT; this forces concrete classes that implement Action to provide an actionPerformed( ) method. The programmer uses the actionPerformed( ) method to implement whatever behavior is desired. For example, if you are creating a Save action, you should put the code that saves the data inside of your actionPerformed( ) method.

When the action is added to an accepting container such as JMenu, JPopupMenu, or JToolBar, the container automatically registers the action as an ActionListener of the GUI component it creates. Consequently, if the GUI component is selected by the user, it simply invokes the actionPerformed( ) method of the action to do its job.

The Action interface defines five constants (shown in Table 3-2), which serve as keys for storing standardized Action properties. The method of storage varies from implementer to implementer, but a Hashtable is common. These properties store information such as the name of the action, its description, and a representative icon. Also, the Action interface defines a boolean property that indicates whether the action is enabled or disabled. Recall that the GUI component created for the action registers itself as a PropertyChangeListener. Hence, if any of these properties are modified, the GUI component is notified and can react accordingly.

Table 3-1. String-based key constants for the Action interface

Constant

Meaning

DEFAULT

Default setting

NAME

Name of the action

SHORT_DESCRIPTION

Short text description of what the action does

LONG_DESCRIPTION

Long text description of what the action does

SMALL_ICON

Represents a small icon; typically used in a toolbar

3.1.2.1 Property

The Action interface defines the property shown in Table 3-2.

Table 3-2. Action property

Property

Data type

get

is

set

Default value

enabled

boolean

 

·

·

 

See also java.awt.ActionListener.

The enabled property defines whether anyone can invoke the action. When this property changes, the action should fire a PropertyChangeEvent describing the change.

Note that the properties whose keys appear in Table 3-1 are not also shown here. These are really properties because changing one should fire a PropertyChangeEvent. However, because they do not use standard accessors, they do not fit the true JavaBeans property model, so we have omitted them from Table 3-2.

3.1.2.2 Methods

public abstract Object getValue(String key)
public abstract void putValue(String key, Object value)

Store various keyed properties for the action. A string-based key is used to index the values. Several string constants representing the keys are shown in Table 3-1. When putValue( ) is called with any property, and the value passed in is different than what was there previously, the implementing object must fire a PropertyChangeEvent describing the change to all registered listeners.

public abstract void actionPerformed(ActionEvent e)

This method is required by the ActionListener interface (it does not actually exist in the Action interface). Any concrete class that implements the Action interface must provide an actionPerformed( ) method that performs whatever task the action is supposed to accomplish.

3.1.2.3 Events

Objects implementing the Action interface must fire a PropertyChangeEvent when any keyed property is changed, or when the action is enabled or disabled. Containers that accept actions typically listen for these PropertyChangeEvent notifications so they can update their own properties or appearances.

public abstract void addPropertyChangeListener(PropertyChangeListener listener)
public abstract void removePropertyChangeListener(PropertyChangeListener listener)

Add or remove the specified PropertyChangeListener from the event listener list.

3.1.3 The AbstractAction Class

The AbstractAction class is an abstract implementation of the Action interface. AbstractAction provides the default functionality for almost all methods in the Action interface. You can extend this class to create your own specific actions. If you do so, the only method for which you must provide an implementation is the actionPerformed( ) method, which provides the functionality for the action. Here is a simple example:

class MyAction extends AbstractAction { public MyAction(String text, Icon icon) { super(text,icon); } public void actionPerformed(ActionEvent e) { System.out.println("Action [" + e.getActionCommand( ) + "]!"); } }

Here, we simply print the action command sent with the ActionEvent. You can add more features based on the contents of the ActionEvent.

3.1.3.1 Properties

The AbstractAction class stores its keyed properties in a Hashtable object. Beyond that, the AbstractAction object contains a few properties, as shown in Table 3-3. The enabled property defines whether the application can invoke the action. When this property changes, AbstractAction fires a PropertyChangeEvent. The mutator for this property, setEnabled( ), is synchronized. If you want a list of the current property listeners, use the propertyChangeListeners property.

Table 3-3. AbstractAction properties

Property

Data type

get

is

set

Default value

enabledb

boolean

 

·

·

true

keysb, 1.3

Object[]

·

   

null

propertyChangeListeners1.4

PropertyChangeListener[]

·

   

Empty array

1.3since 1.3, 1.4since 1.4, bbound

3.1.3.2 Events

The AbstractAction class fires a PropertyChangeEvent when any property in the hashtable is changed or when the action is enabled or disabled.

public void addPropertyChangeListener(PropertyChangeListener listener)
public void removePropertyChangeListener(PropertyChangeListener listener)

Add or remove the specified PropertyChangeListener from the event listener list.

3.1.3.3 Constructors

public AbstractAction( )
public AbstractAction(String name)
public AbstractAction(String name, Icon icon)

The constructors for the AbstractAction object can be used to set the name and icon hashtable properties of the action under the NAME or SMALL_ICON keys, respectively.

3.1.3.4 Methods

public Object getValue(String key)
public void putValue(String key, Object value)

These methods store or retrieve various elements in a private Hashtable. A string-based key is used to index the Hashtable values. See the Action interface earlier in the chapter for an enumeration of common string-based keys.

3.1.3.5 Using an Action

This example creates an Action for both a menu item and a toolbar, displaying both components and allowing the user to click on either one. When the components are clicked, the actionPerformed( ) method of the action is called. Don't worry if you don't understand all the methods behind the toolbar or the menu; these classes are discussed later. For now, it is important to see that selecting either one performs the action.

// ActionExample.java // import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class ActionExample extends JPanel { public JMenuBar menuBar; public JToolBar toolBar; public ActionExample( ) { super(true); // Create a menu bar and give it a bevel border. menuBar = new JMenuBar( ); menuBar.setBorder(new BevelBorder(BevelBorder.RAISED)); // Create a menu and add it to the menu bar. JMenu menu = new JMenu("Menu"); menuBar.add(menu); // Create a toolbar and give it an etched border. toolBar = new JToolBar( ); toolBar.setBorder(new EtchedBorder( )); // Instantiate a sample action with the NAME property of "Download" and the // appropriate SMALL_ICON property. SampleAction exampleAction = new SampleAction("Download", new ImageIcon("action.gif")); // Finally, add the sample action to the menu and the toolbar. These methods // are no longer preferred: // menu.add(exampleAction); // toolBar.add(exampleAction); // Instead, you should create actual menu items and buttons: JMenuItem exampleItem = new JMenuItem(exampleAction); JButton exampleButton = new JButton(exampleAction); menu.add(exampleItem); toolBar.add(exampleButton); } class SampleAction extends AbstractAction { // This is our sample action. It must have an actionPerformed( ) method, which // is called when the action should be invoked. public SampleAction(String text, Icon icon) { super(text,icon); } public void actionPerformed(ActionEvent e) { System.out.println("Action [" + e.getActionCommand( ) + "] performed!"); } } public static void main(String s[]) { ActionExample example = new ActionExample( ); JFrame frame = new JFrame("Action Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setJMenuBar(example.menuBar); frame.getContentPane( ).add(example.toolBar, BorderLayout.NORTH); frame.setSize(200,200); frame.setVisible(true); } }

The preceding example creates a toolbar with a single button and a menu with a single menu item. Both are generated from the SampleAction class and are shown in Figure 3-2.

Figure 3-2. An action in a menu and in a toolbar

Selecting the menu item or clicking on the toolbar button a few times both yield the same results on the console:

Action [Download] performed! Action [Download] performed! Action [Download] performed!

Now for something interesting. You can add the following line to the constructor to disable the action:

exampleAction.setEnabled(false);

With this line, the PropertyChangeEvent propagates to listeners in the menu item and in the toolbar button, causing both components to turn gray and become disabled. Figure 3-3 shows what happens when an action is disabled.

Figure 3-3. A disabled action in a menu and in a toolbar

Of course, you can enable the menu item and toolbar button again at any time with the following line of code:

exampleAction.setEnabled(true);

Upon execution, the property change again propagates, re-enabling both components simultaneously.

Actions also play a critical role in supporting key bindings within components (see Section 3.5.14 later in this chapter).

Категории