1.5 Using Other Swing Features

«« Previous
Next »»

This lesson contains a collection of how-to pages to help you use miscellaneous Swing features.

➤ How to Integrate with the Desktop Class


Java™ Standard Edition version 6 narrows the gap between performance and integration of native applications and Java applications. Along with the new system tray functionality, splash screen support, and enhanced printing for JTables, Java SE version 6 provides the Desktop API (java.awt.Desktop) API, which allows Java applications to interact with default applications associated with specific file types on the host platform.

New functionality is provided by the Desktop class. The API arises from the JDesktop Integration Components (JDIC) project. The goal of the JDIC project is to make "Java technology-based applications first-class citizens" of the desktop, enabling seamless integration. JDIC provides Java applications with access to functionalities and facilities provided by the native desktop. Regarding the new Desktop API, this means that a Java application can perform the following operations:

Launch the host system's default browser with a specific Uniform Resource Identifier (URI)
Launch the host system's default email client
Launch applications to open, edit, or print files associated with those applications

Note: The Desktop API uses the host operating system's file associations to launch applications associated with specific file types. For example, if OpenDocument text (.odt) file extensions are associated with the OpenOffice Writer application, a Java application could launch OpenOffice Writer to open, edit, or even print files with that association. Depending on the host system, different applications may be associated with different actions. For example, if a particular file cannot be printed, check first whether its extension has a printing association on the given operating system.

Use the isDesktopSupported() method to determine whether the Desktop API is available. On the Solaris Operating System and the Linux platform, this API is dependent on Gnome libraries. If those libraries are unavailable, this method will return false. After determining that the Desktop API is supported, that is, the isDesktopSupported() returns true, the application can retrieve a Desktop instance using the static method getDesktop().

If an application runs in an environment without a keyboard, mouse, or monitor (a "headless" environment), the getDesktop() method throws a java.awt.HeadlessException.

Once retrieved, the Desktop instance allows an application to browse, mail, open, edit, or even print a file or URI, but only if the retrieved Desktop instance supports these activities. Each of these activities is called an action, and each is represented as a Desktop.Action enumeration instance:
  • BROWSE — Represents a browse action performed by the host's default browser.
  • MAIL — Represents a mail action performed by the host's default email client.
  • OPEN — Represents an open action performed by an application associated with opening a specific file type.
  • EDIT — Represents an edit action performed by an application associated with editing a specific file type
  • PRINT — Represents a print action performed by an application associated with printing a specific file type.
Different applications may be registered for these different actions even on the same file type. For example, the Firefox browser may be launched for the OPEN action, Emacs for the EDIT action, and yet a different application for the PRINT action. Your host desktop's associations are used to determine which application should be invoked. The ability to manipulate desktop file associations is not possible with the current version of the Desktop API in JDK 6, and those associations can be created or changed only with platform-dependent tools at this time.

The following example shows the capabilities mentioned above.

Try this:

1. Compile and run the example, consult the example index.
2. The DesktopDemo dialog box will appear.

Oracle Java Tutorials and Materials, Oracle Java Certifications

3. Enter an URI value into the URI text field, for example – https://docs.oracle.com/javase/tutorial.
4. Press the Launch Browser button.
5. Ensure that the default browser window opens and the Tutorial main page is loaded.
6. Change URI to an arbitrary value, press the Launch Browser button, and ensure that the web page you requested is loaded successfully.
7. Switch back to the DesktopDemo dialog box and enter a mail recipient name in the E-mail text field. You can also use the mailto scheme supporting CC, BCC, SUBJECT, and BODY fields, for example – duke@example.com?SUBJECT=Hello Duke!.
8. Press the Launch Mail button.
9. The compositing dialog box of the default email client will appear. Be sure that the To and Subject fields are as follows.

Oracle Java Tutorials and Materials, Oracle Java Certifications

10. You can continue to compose a message or try to enter different combinations of the mail schema in the E-mail field.
11. Switch back to the DesktopDemo dialog box and press the ellipsis button to choose any text file.
12. Select either Open, Edit, or Print to set the type of operation, then press the Launch Application button.
13. Be sure that operation completes correctly. Try other file formats, for example .odt, .html, .pdf. 

Note: If you try to edit .pdf file, the DesktopDemo returns the following message: Cannot perform the given operation to the <file name> file

The following code snippets provide more details on the DeskDemo application implementation. The DesktopDemo constructor disables the few components right after instantiating the UI and checks whether the Desktop API is available.

 public DesktopDemo() {
        // init all gui components
        initComponents();
        // disable buttons that launch browser, email client,
        // disable buttons that open, edit, print files
        disableActions();
        // before any Desktop APIs are used, first check whether the API is
        // supported by this particular VM on this particular host
        if (Desktop.isDesktopSupported()) {
            desktop = Desktop.getDesktop();
            // now enable buttons for actions that are supported.
            enableSupportedActions();
        }
        ...

    /**
     * Disable all graphical components until we know
     * whether their functionality is supported.
     */
    private void disableActions() {
        txtBrowserURI.setEnabled(false);
        btnLaunchBrowser.setEnabled(false);
        
        txtMailTo.setEnabled(false);
        btnLaunchEmail.setEnabled(false);
        
        rbEdit.setEnabled(false);
        rbOpen.setEnabled(false);
        rbPrint.setEnabled(false);

        txtFile.setEnabled(false);
        btnLaunchApplication.setEnabled(false);        
    }

   ...
Once a Desktop object is acquired, you can query the object to find out which specific actions are supported. If the Desktop object does not support specific actions, or if the Desktop API itself is unsupported, DesktopDemo simply keeps the affected graphical components disabled.

/**
     * Enable actions that are supported on this host.
     * The actions are the following: open browser, 
     * open email client, and open, edit, and print
     * files using their associated application.
     */
    private void enableSupportedActions() {
        if (desktop.isSupported(Desktop.Action.BROWSE)) {
            txtBrowserURI.setEnabled(true);
            btnLaunchBrowser.setEnabled(true);
        }
        
        if (desktop.isSupported(Desktop.Action.MAIL)) {
            txtMailTo.setEnabled(true);
            btnLaunchEmail.setEnabled(true);
        }

        if (desktop.isSupported(Desktop.Action.OPEN)) {
            rbOpen.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.EDIT)) {
            rbEdit.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.PRINT)) {
            rbPrint.setEnabled(true);
        }
        
        if (rbEdit.isEnabled() || rbOpen.isEnabled() || rbPrint.isEnabled()) {
            txtFile.setEnabled(true);
            btnLaunchApplication.setEnabled(true);
        }
    }

The browse(uri) method can throw a variety of exceptions, including a NullPointerException if the URI is null, and an UnsupportedOperationException if the BROWSE action is unsupported. This method can throw an IOException if the default browser or application cannot be found or launched, and a SecurityException if a security manager denies the invocation.

private void onLaunchBrowser(ActionEvent evt) {
        URI uri = null;
        try {
            uri = new URI(txtBrowserURI.getText());
            desktop.browse(uri);
        } catch(IOException ioe) {
            System.out.println("The system cannot find the " + uri + 
                " file specified");
            //ioe.printStackTrace();
        } catch(URISyntaxException use) {
            System.out.println("Illegal character in path");
            //use.printStackTrace();
        }
    }

Applications can launch the host's default email client, if that action is supported, by calling the mail(uriMailTo) method of this Desktop instance.

private void onLaunchMail(ActionEvent evt) {
        String mailTo = txtMailTo.getText();
        URI uriMailTo = null;
        try {
            if (mailTo.length() > 0) {
                uriMailTo = new URI("mailto", mailTo, null);
                desktop.mail(uriMailTo);
            } else {
                desktop.mail();
            }
        } catch(IOException ioe) {
            ioe.printStackTrace();
        } catch(URISyntaxException use) {
            use.printStackTrace();
        }
    }

Java applications can open, edit, and print files from their associated application using the open(), edit(), and print() methods of the Desktop class, respectively.

private void onLaunchDefaultApplication(ActionEvent evt) {
        String fileName = txtFile.getText();
        File file = new File(fileName);
        
        try {
            switch(action) {
                case OPEN:
                    desktop.open(file);
                    break;
                case EDIT:
                    desktop.edit(file);
                    break;
                case PRINT:
                    desktop.print(file);
                    break;
            }
        } catch (IOException ioe) {
            //ioe.printStackTrace();
            System.out.println("Cannot perform the given operation 
                to the " + file + " file");
        }
    }

The complete code for this demo is available in the DesktopDemo.java file.

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;


public class DesktopDemo extends JFrame {
    
    JButton btnLaunchApplication = new JButton("Launch Application");
    JButton btnLaunchBrowser = new JButton("Launch Browser");
    JButton btnLaunchEmail = new JButton();
    JRadioButton rbEdit = new JRadioButton("Edit");
    JRadioButton rbOpen = new JRadioButton("Open", true);
    JRadioButton rbPrint = new JRadioButton("Print");
    JTextField txtBrowserURI = new JTextField();
    JTextField txtMailTo = new JTextField();
    JTextField txtFile = new JTextField();
    ButtonGroup bgAppAction = new ButtonGroup();
    JLabel lblMailRecipient = new JLabel("E-mail:");
    JLabel lblBrowserUri = new JLabel("URI:");
    JLabel lblFile = new JLabel("File:");
    JButton btnFile = new JButton("...");
    JLabel emptyLabel = new JLabel(" ");
    JPanel conLeft = new JPanel();
    JPanel conCenter = new JPanel();
    JPanel conRight = new JPanel();
    JFileChooser fc = new JFileChooser();
    File file;
    
    private Desktop desktop;
    private Desktop.Action action = Desktop.Action.OPEN;
    
    
    /**
     * Creates new form DesktopDemo
     */
    public DesktopDemo() {
        // init all gui components
        initComponents();
        // disable buttons that launch browser, email client,
        // disable buttons that open, edit, print files
        disableActions();
        // before any Desktop APIs are used, first check whether the API is
        // supported by this particular VM on this particular host
        if (Desktop.isDesktopSupported()) {
            desktop = Desktop.getDesktop();
            // now enable buttons for actions that are supported.
            enableSupportedActions();
        }
        loadFrameIcon();
        setResizable(false);
    }
    
    public static void main(String args[]) {
        /* Use an appropriate Look and Feel */
        try {
            //UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
            //UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
            UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
        } catch (UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        } catch (InstantiationException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        /* Turn off metal's use of bold fonts */
        UIManager.put("swing.boldMetal", Boolean.FALSE);
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new DesktopDemo().setVisible(true);
            }
        });
    }
    
    /** Create and show components
     */
    private void initComponents() {
        
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setTitle("DesktopDemo");
        txtBrowserURI.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onLaunchBrowser(null);
            }
        });
        
        btnLaunchBrowser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                onLaunchBrowser(evt);
            }
        });
        
        
        txtMailTo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onLaunchMail(null);
            }
        });
        
        btnLaunchEmail.setText("Launch Mail");
        btnLaunchEmail.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                onLaunchMail(evt);
            }
        });
        
        
        txtFile.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onLaunchDefaultApplication(null);
            }
        });
        
        rbOpen.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                onOpenAction(evt);
            }
        });
        
        
        rbEdit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                onEditAction(evt);
            }
        });
        
        
        rbPrint.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                onPrintAction(evt);
            }
        });
        
        btnLaunchApplication.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                onLaunchDefaultApplication(evt);
            }
        });
        
        btnFile.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                onChooseFile(evt);
            }
        });
        
        Container conFrame = this.getContentPane();
        
        bgAppAction.add(rbOpen);
        bgAppAction.add(rbEdit);
        bgAppAction.add(rbPrint);
        
        // Components layouting
        
        GroupLayout layout = new GroupLayout(conFrame);
        conFrame.setLayout(layout);
        layout.setAutoCreateContainerGaps(true);
        layout.setAutoCreateGaps(true);
        
        GroupLayout.SequentialGroup majorHGroup = layout.createSequentialGroup();
        
        // Horizontal group
        
        GroupLayout.ParallelGroup lblHGroup =
                layout.createParallelGroup(GroupLayout.Alignment.LEADING);
        lblHGroup.addComponent(lblBrowserUri, GroupLayout.Alignment.TRAILING);
        lblHGroup.addComponent(lblMailRecipient, GroupLayout.Alignment.TRAILING);
        lblHGroup.addComponent(lblFile, GroupLayout.Alignment.TRAILING);
        
        GroupLayout.ParallelGroup txtFieldsHGroup =
                layout.createParallelGroup(GroupLayout.Alignment.LEADING);
        txtFieldsHGroup.addComponent(txtMailTo);
        txtFieldsHGroup.addComponent(txtBrowserURI);
        GroupLayout.SequentialGroup rbHGroup = layout.createSequentialGroup();
        rbHGroup.addComponent(rbOpen);
        rbHGroup.addComponent(rbEdit);
        rbHGroup.addComponent(rbPrint);
        txtFieldsHGroup.addGroup(rbHGroup);
        GroupLayout.SequentialGroup fileHGroup = layout.createSequentialGroup();
        fileHGroup.addComponent(txtFile);
        fileHGroup.addComponent(btnFile);
        txtFieldsHGroup.addGroup(fileHGroup);
        
        GroupLayout.ParallelGroup btnHGroup =
                layout.createParallelGroup(GroupLayout.Alignment.LEADING);
        btnHGroup.addComponent(btnLaunchBrowser);
        btnHGroup.addComponent(btnLaunchEmail);
        btnHGroup.addComponent(btnLaunchApplication);
        
        majorHGroup.addGroup(lblHGroup);
        majorHGroup.addGroup(txtFieldsHGroup);
        majorHGroup.addGroup(btnHGroup);
        
        layout.setHorizontalGroup(majorHGroup);
        
        // Vertical group
        
        GroupLayout.SequentialGroup majorVGroup = layout.createSequentialGroup();
        
        GroupLayout.ParallelGroup uriVGroup =
                layout.createParallelGroup(GroupLayout.Alignment.BASELINE);
        uriVGroup.addComponent(lblBrowserUri);
        uriVGroup.addComponent(txtBrowserURI);
        uriVGroup.addComponent(btnLaunchBrowser);
        
        GroupLayout.ParallelGroup mailVGroup =
                layout.createParallelGroup(GroupLayout.Alignment.BASELINE);
        mailVGroup.addComponent(lblMailRecipient);
        mailVGroup.addComponent(txtMailTo);
        mailVGroup.addComponent(btnLaunchEmail);
        
        GroupLayout.ParallelGroup rbVGroup =
                layout.createParallelGroup(GroupLayout.Alignment.BASELINE);
        rbVGroup.addComponent(rbOpen);
        rbVGroup.addComponent(rbEdit);
        rbVGroup.addComponent(rbPrint);
        
        GroupLayout.ParallelGroup fileVGroup =
                layout.createParallelGroup(GroupLayout.Alignment.BASELINE);
        fileVGroup.addComponent(lblFile);
        fileVGroup.addComponent(btnLaunchApplication);
        fileVGroup.addComponent(txtFile);
        fileVGroup.addComponent(btnFile);
        
        majorVGroup.addGroup(uriVGroup);
        majorVGroup.addGroup(mailVGroup);
        majorVGroup.addGroup(rbVGroup);
        majorVGroup.addGroup(fileVGroup);
        
        layout.setVerticalGroup(majorVGroup);
        
        pack();
    }
    
    /**
     * Load the "desktop" icon into our frame window.
     */
    private void loadFrameIcon() {
        URL imgUrl = null;
        ImageIcon imgIcon = null;
        
        imgUrl = DesktopDemo.class.getResource("images/desk32.gif");
        imgIcon = new ImageIcon(imgUrl);
        Image img = imgIcon.getImage();
        this.setIconImage(img);
    }
    
    /*
     * Set the Desktop.Action to PRINT before invoking
     * the default application.
     */
    private void onPrintAction(ActionEvent evt) {
        action = Desktop.Action.PRINT;
    }
    
    /**
     * Set the Desktop.Action to EDIT before invoking
     * the default application.
     */
    private void onEditAction(ActionEvent evt) {
        action = Desktop.Action.EDIT;
    }
    
    /**
     * Set the Desktop.Action to OPEN before invoking
     * the default application.
     */
    private void onOpenAction(ActionEvent evt) {
        action = Desktop.Action.OPEN;
    }
    
    /**
     * Launch the default application associated with a specific
     * filename using the preset Desktop.Action.
     *
     */
    private void onLaunchDefaultApplication(ActionEvent evt) {
        String fileName = txtFile.getText();
        File file = new File(fileName);
        
        try {
            switch(action) {
                case OPEN:
                    desktop.open(file);
                    break;
                case EDIT:
                    desktop.edit(file);
                    break;
                case PRINT:
                    desktop.print(file);
                    break;
            }
        } catch (IOException ioe) {
            //ioe.printStackTrace();
            System.out.println("Cannot perform the given operation to the " + file + " file");
        }
    }
    
    /**
     * Launch the default email client using the "mailto"
     * protocol and the text supplied by the user.
     *
     */
    private void onLaunchMail(ActionEvent evt) {
        String mailTo = txtMailTo.getText();
        URI uriMailTo = null;
        try {
            if (mailTo.length() > 0) {
                uriMailTo = new URI("mailto", mailTo, null);
                desktop.mail(uriMailTo);
            } else {
                desktop.mail();
            }
        } catch(IOException ioe) {
            ioe.printStackTrace();
        } catch(URISyntaxException use) {
            use.printStackTrace();
        }
    }
    
    /**
     * Launch the default browser with the text provided by the
     * user.
     *
     */
    private void onLaunchBrowser(ActionEvent evt) {
        URI uri = null;
        try {
            uri = new URI(txtBrowserURI.getText());
            desktop.browse(uri);
        } catch(IOException ioe) {
            System.out.println("The system cannot find the " + uri + " file specified");
            //ioe.printStackTrace();
        } catch(URISyntaxException use) {
            System.out.println("Illegal character in path");
            //use.printStackTrace();
        }
    }
    
    private void onChooseFile(ActionEvent evt) {
        if (evt.getSource() == btnFile) {
            int returnVal = fc.showOpenDialog(DesktopDemo.this);
            if (returnVal == JFileChooser.APPROVE_OPTION){
                file = fc.getSelectedFile();
                txtFile.setText(file.getAbsolutePath());
            }
        }
    }
    
    /**
     * Enable actions that are supported on this host.
     * The actions are: open browser, open email client, and
     * open, edit, and print files using their associated application
     */
    
    private void enableSupportedActions() {
        if (desktop.isSupported(Desktop.Action.BROWSE)) {
            txtBrowserURI.setEnabled(true);
            btnLaunchBrowser.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.MAIL)) {
            txtMailTo.setEnabled(true);
            btnLaunchEmail.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.OPEN)) {
            rbOpen.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.EDIT)) {
            rbEdit.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.PRINT)) {
            rbPrint.setEnabled(true);
        }
        if (rbEdit.isEnabled() || rbOpen.isEnabled() || rbPrint.isEnabled()) {
            txtFile.setEnabled(true);
            btnLaunchApplication.setEnabled(true);
            btnFile.setEnabled(true);
        }
    }
    
    /**
     * Disable all graphical components until we know
     * whether their functionality is supported.
     */
    private void disableActions() {
        txtBrowserURI.setEnabled(false);
        btnLaunchBrowser.setEnabled(false);
        
        txtMailTo.setEnabled(false);
        btnLaunchEmail.setEnabled(false);
        
        rbEdit.setEnabled(false);
        rbOpen.setEnabled(false);
        rbPrint.setEnabled(false);
        
        txtFile.setEnabled(false);
        btnLaunchApplication.setEnabled(false);
        btnFile.setEnabled(false);
    }
}

The Desktop API


The Desktop class allows Java applications to launch the native desktop applications that handle URIs or files.
 Method Purpose
isDesktopSupported() Tests whether this class is supported on the current platform. If it is supported, use getDesktop() to retrieve an instance.
getDesktop() Returns the Desktop instance of the current browser context. On some platforms the Desktop API may not be supported. Use the isDesktopSupported() method to determine if the current desktop is supported.
isSupported(Desktop.Action) Tests whether an action is supported on the current platform. Use the following constans of the Desktop.Action enum: BROWSE, EDIT, MAIL, OPEN, PRINT.
browse(URI) Launches the default browser to display a URI. If the default browser is not able to handle the specified URI, the application registered for handling URIs of the specified type is invoked. The application is determined from the protocol and path of the URI, as defined by the URI class.
mail(URI) Launches the mail composing window of the user default mail client, filling the message fields specified by a mailto: URI.
open(File) Launches the associated application to open a file.
edit(File) Launches the associated editor application and opens a file for editing.
print(File) Prints a file with the native desktop printing facility, using the associated application's print command.

Examples That Use Desktop API


The following table lists the example that uses the Desktop class integration.

ExampleWhere Described - Notes
DesktopDemoThis section - Launches the host system's default browser with the specified URI and default email client; launches an application to open, edit, or print a file.

➤ How to Create Translucent and Shaped Windows


As of the Java Platform, Standard Edition 6 (Java SE 6) Update 10 release, you can add translucent and shaped windows to your Swing applications. This page covers the following topics:

  • Supported Capabilities

This functionality, which is part of the public AWT package in the JDK 7 release, takes three forms, as follows:

You can create a window with uniform translucency, where each pixel has the same translucency (or alpha) value. The following screen capture shows a window with 45 percent translucency.

Oracle Java Tutorials and Materials, Oracle Java Certifications

You can create a window with per-pixel translucency, where each pixel has its own alpha value. With this feature you can, for example, create a window that fades away to nothing by defining a gradient in the alpha values. The following screen capture shows a window with gradient translucency from the top (fully translucent) to the bottom (fully opaque).

Oracle Java Tutorials and Materials, Oracle Java Certifications

You can create a window with any Shape object that you can define. Shaped windows can be opaque, or they can use uniform, or per-pixel, translucency. The following screen capture shows an oval-shaped window with 30 percent translucency.

Oracle Java Tutorials and Materials, Oracle Java Certifications

  • Determining a Platform's Capabilities

Not all platforms support all of these capabilities. An UnsupportedOperationException exception is thrown when code attempts to invoke the setShape or setOpacity methods on a platform that does not support these capabilities. Therefore, it is best practice to first check that the platform supports the capability that you want to implement. The GraphicsDevice class provides the isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency) method that you can use for this purpose. You pass one of three enum values, defined in GraphicsDevice.WindowTranslucency, to this method:

  1. TRANSLUCENT – The underlying platform supports windows with uniform translucency, where each pixel has the same alpha value.
  2. PERPIXEL_TRANSLUCENT – The underlying platform supports windows with per-pixel translucency. This capability is required to implement windows that fade away.
  3. PERPIXEL_TRANSPARENT – The underlying platform supports shaped windows.
The GraphicsConfiguration class also provides the isTranslucencyCapable method to determine if PERPIXEL_TRANSLUCENT translucency is supported by the given GraphicsConfiguration object.

Version note: The translucent and shaped window API was first added to the Java SE 6 Update 10 release as a private API. This functionality was moved to the public AWT package in the JDK 7 release. This tutorial describes the API that is available in the JDK 7 release. See Java SE 6 Update 10 API for a mapping of the private API in the Java SE 6 Update 10 release to the public API in the JDK 7 release.

The following code shows how to check for all three capabilities:
import static java.awt.GraphicsDevice.WindowTranslucency.*;

// Determine what the default GraphicsDevice can support.
GraphicsEnvironment ge =
    GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();

boolean isUniformTranslucencySupported =
    gd.isWindowTranslucencySupported(TRANSLUCENT);
boolean isPerPixelTranslucencySupported =
    gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);
boolean isShapedWindowSupported =
    gd.isWindowTranslucencySupported(PERPIXEL_TRANSPARENT);

Note: None of these capabilities work on windows in full-screen mode. Invoking any of the relevant methods while in full-screen mode causes an IllegalComponentStateException exception to be thrown.

  • How to Implement Uniform Translucency

You can create a window where each pixel has the same translucency by invoking the setOpacity(float) method in the Window class. The float argument passed to this method represents the translucency of the window and should be a value between 0 and 1, inclusive. The smaller the number, the more transparent the window. There is also a corresponding getOpacity method.
The TranslucentWindowDemo.java example creates a window that is 55 percent opaque (45 percent translucent). If the underlying platform does not support translucent windows, the example exits. The code relating to opacity is shown in bold.

import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class TranslucentWindowDemo extends JFrame {
    public TranslucentWindowDemo() {
        super("TranslucentWindow");
        setLayout(new GridBagLayout());

        setSize(300,200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Add a sample button.
        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine if the GraphicsDevice supports translucency.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();

        //If translucent windows aren't supported, exit.
        if (!gd.isWindowTranslucencySupported(TRANSLUCENT)) {
            System.err.println(
                "Translucency is not supported");
                System.exit(0);
        }
        
        JFrame.setDefaultLookAndFeelDecorated(true);

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                TranslucentWindowDemo tw = new TranslucentWindowDemo();

                // Set the window to 55% opaque (45% translucent).
                tw.setOpacity(0.55f);

                // Display the window.
                tw.setVisible(true);
            }
        });
    }
}

Note that the button is also affected by the uniform translucency. Setting the opacity affects the whole window, including any components that the window contains.

  • How to Implement Per-Pixel Translucency

Creating a window that uses per-pixel translucency involves defining alpha values over the rectangular area that the window occupies. When a pixel's alpha value is zero, that pixel is fully transparent. When a pixel's alpha value is 255, that pixel is fully opaque. When a pixel's alpha value is 128, that pixel is 50 percent translucent, and so on. An easy way to create a smooth interpolation between alpha values is to use the GradientPaint class. The included example uses this approach.

Invoking setBackground(new Color(0,0,0,0)) on the window causes the software to use the alpha values to render per-pixel translucency. In fact, invoking setBackground(new Color(0,0,0,alpha), where alpha is less than 255, installs per-pixel transparency. So, if you invoke setBackground(new Color(0,0,0,128)) and do nothing else, the window is rendered with 50 percent translucency for each background pixel. However, if you are creating your own range of alpha values, you most likely will want an alpha value of 0.

While not prohibited by the public API, you will generally want to enable per-pixel translucency on undecorated windows. In most cases, using per-pixel translucency on decorated windows does not make sense. Doing so can disable the decorations, or cause other platform-dependent side effects.

To determine if a window is using per-pixel translucency, you can use the isOpaque method.

An example follows. First, here are the steps required to implement the example:

  1. Invoke setBackground(new Color(0,0,0,0)) on the window.
  2. Create a JPanel instance that overrides the paintComponent method.
  3. In the paintComponent method, create a GradientPaint instance.
  4. In the example, the top of the rectangle has an alpha value of 0 (the most transparent) and the bottom has an alpha value of 255 (the most opaque). The GradientPaint class smoothly interpolates the alpha values from the top to the bottom of the rectangle.
  5. Set the GradientPaint instance as the panel's paint method.
If the underlying platform does not support per-pixel translucency, this example exits. The code specifically relating to creating the gradient window is shown in bold.

import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class GradientTranslucentWindowDemo extends JFrame {
    public GradientTranslucentWindowDemo() {
        super("GradientTranslucentWindow");

        setBackground(new Color(0,0,0,0));
        setSize(new Dimension(300,200));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                if (g instanceof Graphics2D) {
                    final int R = 240;
                    final int G = 240;
                    final int B = 240;

                    Paint p =
                        new GradientPaint(0.0f, 0.0f, new Color(R, G, B, 0),
                            0.0f, getHeight(), new Color(R, G, B, 255), true);
                    Graphics2D g2d = (Graphics2D)g;
                    g2d.setPaint(p);
                    g2d.fillRect(0, 0, getWidth(), getHeight());
                }
            }
        };
        setContentPane(panel);
        setLayout(new GridBagLayout());
        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        boolean isPerPixelTranslucencySupported = 
            gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);

        //If translucent windows aren't supported, exit.
        if (!isPerPixelTranslucencySupported) {
            System.out.println(
                "Per-pixel translucency is not supported");
                System.exit(0);
        }

        JFrame.setDefaultLookAndFeelDecorated(true);

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                GradientTranslucentWindowDemo gtw = new
                    GradientTranslucentWindowDemo();

                // Display the window.
                gtw.setVisible(true);
            }
        });
    }
}

Note that the button is not affected by the per-pixel translucency. Setting the per-pixel translucency affects the background pixels only. If you want a window that has a uniformly translucent effect on the background pixels only, you can invoke setBackground(new Color(0,0,0,alpha)) where alpha specifies your desired translucency.

  • How to Implement a Shaped Window


You can create a shaped window by invoking the setShape(Shape) method in the Window class. The Shape argument that is passed to the method determines how the window is clipped. When a shape is set on a window, the window decorations are not re-formed to the new shape, so setting a shape works best on undecorated windows.
The best practice for setting the window's shape is to invoke setShape in the componentResized method of the component event listener. This practice will ensure that the shape is correctly calculated for the actual size of the window. The following example uses this approach.

The ShapedWindowDemo.java example creates an oval-shaped window with 70 percent opacity. If the underlying platform does not support shaped windows, the example exits. If the underlying platform does not support translucency, the example uses a standard opaque window. You could modify this example to create a shaped window that also uses per-pixel translucency.

The code relating to shaping the window is shown in bold.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class ShapedWindowDemo extends JFrame {
    public ShapedWindowDemo() {
        super("ShapedWindow");
        setLayout(new GridBagLayout());

        // It is best practice to set the window's shape in
        // the componentResized method.  Then, if the window
        // changes size, the shape will be correctly recalculated.
        addComponentListener(new ComponentAdapter() {
            // Give the window an elliptical shape.
            // If the window is resized, the shape is recalculated here.
            @Override
            public void componentResized(ComponentEvent e) {
                setShape(new Ellipse2D.Double(0,0,getWidth(),getHeight()));
            }
        });

        setUndecorated(true);
        setSize(300,200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        final boolean isTranslucencySupported = 
            gd.isWindowTranslucencySupported(TRANSLUCENT);

        //If shaped windows aren't supported, exit.
        if (!gd.isWindowTranslucencySupported(PERPIXEL_TRANSPARENT)) {
            System.err.println("Shaped windows are not supported");
            System.exit(0);
        }

        //If translucent windows aren't supported, 
        //create an opaque window.
        if (!isTranslucencySupported) {
            System.out.println(
                "Translucency is not supported, creating an opaque window");
        }

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ShapedWindowDemo sw = new ShapedWindowDemo();

                // Set the window to 70% translucency, if supported.
                if (isTranslucencySupported) {
                    sw.setOpacity(0.7f);
                }

                // Display the window.
                sw.setVisible(true);
            }
        });
    }
}

  • Java SE Release 6 Update 10 API

Method in Java SE 6 Update 10 JDK 7 Equivalent
AWTUtilities.isTranslucencySupported(Translucency) GraphicsDevice.isWindowTranslucencySupported(WindowTranslucency)
AWTUtilities.isTranslucencyCapable(GraphicsConfiguration) GraphicsConfiguration.isTranslucencyCapable()
AWTUtilities.setWindowOpacity(Window, float) Window.setOpacity(float)
AWTUtilities.setWindowShape(Window, Shape) Window.setShape(Shape)
AWTUtilities.setWindowOpaque(boolean) Window.setBackground(Color) Passing new Color(0,0,0,alpha) to this method, where alpha is less than 255, installs per-pixel translucency.


➤ How to Decorate Components with the JLayer Class


The JLayer class is a flexible and powerful decorator for Swing components. It enables you to draw on components and respond to component events without modifying the underlying component directly.

The JLayer class in Java SE 7 is similar in spirit to the JxLayer project project at java.net. The JLayer class was initially based on the JXLayer project, but its API evolved separately.


For a brief introduction to the material on this page, watch the following video.


This document describes examples that show the power of the JLayer class. Full source code is available.

  • Using the JLayer Class


The javax.swing.JLayer class is half of a team. The other half is the javax.swing.plaf.LayerUI class. Suppose you want to do some custom drawing atop a JButton object (decorate the JButton object). The component you want to decorate is the target.
  1. Create the target component.
  2. Create an instance of a LayerUI subclass to do the drawing.
  3. Create a JLayer object that wraps the target and the LayerUI object.
  4. Use the JLayer object in your user interface just as you would use the target component.
For example, to add an instance of a JPanel subclass to a JFrame object, you would do something similar to this:

JFrame f = new JFrame();

JPanel panel = createPanel();

f.add (panel);
To decorate the JPanel object, do something similar to this instead:

JFrame f = new JFrame();

JPanel panel = createPanel();
LayerUI<JPanel> layerUI = new MyLayerUISubclass();
JLayer<JPanel> jlayer = new JLayer<JPanel>(panel, layerUI);

f.add (jlayer);
Use generics to ensure that the JPanel object and the LayerUI object are for compatible types. In the previous example, both the JLayer object and the LayerUI object are used with the JPanel class.

The JLayer class is usually generified with the exact type of its view component, while the LayerUI class is designed to be used with JLayer classes of its generic parameter or any of its ancestors.

For example, a LayerUI<JComponent> object can be used with a JLayer<AbstractButton> object.

A LayerUI object is responsible for custom decoration and event handling for a JLayer object. When you create an instance of a LayerUI subclass, your custom behavior can be applicable to every JLayer object with an appropriate generic type. That is why the JLayer class is final; all custom behavior is encapsulated in your LayerUI subclass, so there is no need to make a JLayer subclass.

  • Using the LayerUI Class


The LayerUI class inherits most of its behavior from the ComponentUI class. Here are the most commonly overridden methods:
  1. The paint(Graphics g, JComponent c) method is called when the target component needs to be drawn. To render the component in the same way that Swing renders it, call the super.paint(g, c) method.
  2. The installUI(JComponent c) method is called when an instance of your LayerUI subclass is associated with a component. Perform any necessary initializations here. The component that is passed in is the corresponding JLayer object. Retrieve the target component with the JLayer class' getView() method.
  3. The uninstallUI(JComponent c) method is called when an instance of your LayerUI subclass is no longer associated with the given component. Clean up here if necessary.

  • Drawing on Components


To use the JLayer class, you need a good LayerUI subclass. The simplest kinds of LayerUI classes change how components are drawn. Here is one, for example, that paints a transparent color gradient on a component.

class WallpaperLayerUI extends LayerUI<JComponent> {
  @Override
  public void paint(Graphics g, JComponent c) {
    super.paint(g, c);

    Graphics2D g2 = (Graphics2D) g.create();

    int w = c.getWidth();
    int h = c.getHeight();
    g2.setComposite(AlphaComposite.getInstance(
            AlphaComposite.SRC_OVER, .5f));
    g2.setPaint(new GradientPaint(0, 0, Color.yellow, 0, h, Color.red));
    g2.fillRect(0, 0, w, h);

    g2.dispose();
  }
}

The paint() method is where the custom drawing takes place. The call to the super.paint() method draws the contents of the JPanel object. After setting up a 50% transparent composite, the color gradient is drawn.

After the LayerUI subclass is defined, using it is simple. Here is some source code that uses the WallpaperLayerUI class:

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.LayerUI;

public class Wallpaper {
  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createUI();
      }
    });
  }

  public static void createUI() {
    JFrame f = new JFrame("Wallpaper");
    
    JPanel panel = createPanel();
    LayerUI<JComponent> layerUI = new WallpaperLayerUI();
    JLayer<JComponent> jlayer = new JLayer<JComponent>(panel, layerUI);
    
    f.add (jlayer);
    
    f.setSize(300, 200);
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
    f.setLocationRelativeTo (null);
    f.setVisible (true);
  }

  private static JPanel createPanel() {
    JPanel p = new JPanel();

    ButtonGroup entreeGroup = new ButtonGroup();
    JRadioButton radioButton;
    p.add(radioButton = new JRadioButton("Beef", true));
    entreeGroup.add(radioButton);
    p.add(radioButton = new JRadioButton("Chicken"));
    entreeGroup.add(radioButton);
    p.add(radioButton = new JRadioButton("Vegetable"));
    entreeGroup.add(radioButton);

    p.add(new JCheckBox("Ketchup"));
    p.add(new JCheckBox("Mustard"));
    p.add(new JCheckBox("Pickles"));

    p.add(new JLabel("Special requests:"));
    p.add(new JTextField(20));

    JButton orderButton = new JButton("Place Order");
    p.add(orderButton);

    return p;
  }
}

Here is the result:

Oracle Java tutorials and Materials

Source code:


Wallpaper NetBeans Project
Wallpaper.java

The LayerUI class' paint() method gives you complete control over how a component is drawn. Here is another LayerUI subclass that shows how the entire contents of a panel can be modified using Java 2D image processing:

class BlurLayerUI extends LayerUI<JComponent> {
  private BufferedImage mOffscreenImage;
  private BufferedImageOp mOperation;

  public BlurLayerUI() {
    float ninth = 1.0f / 9.0f;
    float[] blurKernel = {
      ninth, ninth, ninth,
      ninth, ninth, ninth,
      ninth, ninth, ninth
    };
    mOperation = new ConvolveOp(
            new Kernel(3, 3, blurKernel),
            ConvolveOp.EDGE_NO_OP, null);
  }

  @Override
  public void paint (Graphics g, JComponent c) {
    int w = c.getWidth();
    int h = c.getHeight();

    if (w == 0 || h == 0) {
      return;
    }

    // Only create the offscreen image if the one we have
    // is the wrong size.
    if (mOffscreenImage == null ||
            mOffscreenImage.getWidth() != w ||
            mOffscreenImage.getHeight() != h) {
      mOffscreenImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    }

    Graphics2D ig2 = mOffscreenImage.createGraphics();
    ig2.setClip(g.getClip());
    super.paint(ig2, c);
    ig2.dispose();

    Graphics2D g2 = (Graphics2D)g;
    g2.drawImage(mOffscreenImage, mOperation, 0, 0);
  }
}

In the paint() method, the panel is rendered into an offscreen image. The offscreen image is processed with a convolution operator, then drawn to the screen.

The entire user interface is still live, just blurry:

Oracle Java tutorials and Materials

Source code:

Myopia NetBeans Project
Myopia.java

  • Responding to Events

Your LayerUI subclass can also receive all of the events of its corresponding component. However, the JLayer instance must register its interest in specific types of events. This happens with the JLayer class' setLayerEventMask() method. Typically, however, this call is made from initialization performed in the LayerUI class' installUI() method.

For example, the following excerpt shows a portion of a LayerUI subclass that registers to receive mouse and mouse motion events.

public void installUI(JComponent c) {
  super.installUI(c);
  JLayer jlayer = (JLayer)c;
  jlayer.setLayerEventMask(
    AWTEvent.MOUSE_EVENT_MASK |
    AWTEvent.MOUSE_MOTION_EVENT_MASK
  );
}
All events going to your JLayer subclass get routed to an event handler method whose name matches the event type. For example, you can respond to mouse and mouse motion events by overriding corresponding methods:

protected void processMouseEvent(MouseEvent e, JLayer l) {
  // ...
}

protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
  // ...
}
The following is a LayerUI subclass that draws a translucent circle wherever the mouse moves inside a panel.

class SpotlightLayerUI extends LayerUI<JPanel> {
  private boolean mActive;
  private int mX, mY;

  @Override
  public void installUI(JComponent c) {
    super.installUI(c);
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(
      AWTEvent.MOUSE_EVENT_MASK |
      AWTEvent.MOUSE_MOTION_EVENT_MASK
    );
  }

  @Override
  public void uninstallUI(JComponent c) {
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(0);
    super.uninstallUI(c);
  }

  @Override
  public void paint (Graphics g, JComponent c) {
    Graphics2D g2 = (Graphics2D)g.create();

    // Paint the view.
    super.paint (g2, c);

    if (mActive) {
      // Create a radial gradient, transparent in the middle.
      java.awt.geom.Point2D center = new java.awt.geom.Point2D.Float(mX, mY);
      float radius = 72;
      float[] dist = {0.0f, 1.0f};
      Color[] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
      RadialGradientPaint p =
          new RadialGradientPaint(center, radius, dist, colors);
      g2.setPaint(p);
      g2.setComposite(AlphaComposite.getInstance(
          AlphaComposite.SRC_OVER, .6f));
      g2.fillRect(0, 0, c.getWidth(), c.getHeight());
    }

    g2.dispose();
  }

  @Override
  protected void processMouseEvent(MouseEvent e, JLayer l) {
    if (e.getID() == MouseEvent.MOUSE_ENTERED) mActive = true;
    if (e.getID() == MouseEvent.MOUSE_EXITED) mActive = false;
    l.repaint();
  }

  @Override
  protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
    Point p = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
    mX = p.x;
    mY = p.y;
    l.repaint();
  }
}

The mActive variable indicates whether or not the mouse is inside the coordinates of the panel. In the installUI() method, the setLayerEventMask() method is called to indicate the LayerUI subclass' interest in receiving mouse and mouse motion events.

In the processMouseEvent() method, the mActive flag is set depending on the position of the mouse. In the processMouseMotionEvent() method, the coordinates of the mouse movement are stored in the mX and mY member variables so that they can be used later in the paint() method.

The paint() method shows the default appearance of the panel, then overlays a radial gradient for a spotlight effect:

Oracle Java tutorials and Materials

Source code:

Diva NetBeans Project
Diva.java

  • Animating a Busy Indicator

This example is an animated busy indicator. It demonstrates animation in a LayerUI subclass and features a fade-in and fade-out. It is more complicated that the previous examples, but it is based on the same principle of defining a paint() method for custom drawing.

Click the Place Order button to see the busy indicator for 4 seconds. Notice how the panel is grayed out and the indicator spins. The elements of the indicator have varying levels of transparency.

The LayerUI subclass, the WaitLayerUI class, shows how to fire property change events to update the component. The WaitLayerUI class uses a Timer object to update its state 24 times a second. This happens in the timer's target method, the actionPerformed() method.

The actionPerformed() method uses the firePropertyChange() method to indicate that the internal state was updated. This triggers a call to the applyPropertyChange() method, which repaints the JLayer object:

Oracle Java tutorials and Materials

Source code:

TapTapTap NetBeans Project
TapTapTap.java

  • Validating Text Fields

The final example in this document shows how the JLayer class can be used to decorate text fields to show if they contain valid data. While the other examples use the JLayer class to wrap panels or general components, this example shows how to wrap a JFormattedTextField component specifically. It also demonstrates that a single LayerUI subclass implementation can be used for multiple JLayer instances.

The JLayer class is used to provide a visual indication for fields that have invalid data. When the ValidationLayerUI class paints the text field, it draws a red X if the field contents cannot be parsed. Here is an example:

Oracle Java tutorials and Materials

Source code:

FieldValidator NetBeans Project
FieldValidator.java

➤ How to Use Actions


An Action can be used to separate functionality and state from a component. For example, if you have two or more components that perform the same function, consider using an Action object to implement the function. An Action object is an action listener that provides not only action-event handling, but also centralized handling of the state of action-event-firing components such as tool bar buttons, menu items, common buttons, and text fields. The state that an action can handle includes text, icon, mnemonic, enabled, and selected status.

You typically attach an action to a component using the setAction method. Here's what happens when setAction is invoked on a component:
  • The component's state is updated to match the state of the Action. For example, if the Action's text and icon values were set, the component's text and icon are set to those values.
  • The Action object is registered as an action listener on the component.
  • If the state of the Action changes, the component's state is updated to match the Action. For example, if you change the enabled status of the action, all components it's attached to change their enabled states to match the action.

Here's an example of creating a tool-bar button and menu item that perform the same function:

Action leftAction = new LeftAction(); //LeftAction code is shown later
...
button = new JButton(leftAction)
...
menuItem = new JMenuItem(leftAction);

To create an Action object, you generally create a subclass of AbstractAction and then instantiate it. In your subclass, you must implement the actionPerformed method to react appropriately when the action event occurs. Here's an example of creating and instantiating an AbstractAction subclass:

leftAction = new LeftAction("Go left", anIcon,
             "This is the left button.",
             new Integer(KeyEvent.VK_L));
...
class LeftAction extends AbstractAction {
    public LeftAction(String text, ImageIcon icon,
                      String desc, Integer mnemonic) {
        super(text, icon);
        putValue(SHORT_DESCRIPTION, desc);
        putValue(MNEMONIC_KEY, mnemonic);
    }
    public void actionPerformed(ActionEvent e) {
        displayResult("Action for first button/menu item", e);
    }
}

When the action created by the preceding code is attached to a button and a menu item, the button and menu item display the text and icon associated with the action. The L character is used for mnemonics on the button and menu item, and their tool-tip text is set to the SHORT_DESCRIPTION string followed by a representation of the mnemonic key.

For example, we have provided a simple example, ActionDemo.java, which defines three actions. Each action is attached to a button and a menu item. Thanks to the mnemonic values set for each button's action, the key sequence Alt-L activates the left button, Alt-M the middle button, and Alt-R the right button. The tool tip for the left button displays This is the left button. Alt-L. All of this configuration occurs automatically, without the program making explicit calls to set the mnemonic or tool-tip text. As we'll show later, the program does make calls to set the button text, but only to avoid using the values already set by the actions.

Using Other Swing Features

Try this: 

1. Click the Launch button to run ActionDemo using Java™ Web Start (download JDK 7 or later).

2. Choose the top item from the left menu (Menu > Go left).
The text area displays some text identifying both the event source and the action listener that received the event.

3. Click the leftmost button in the tool bar.
The text area again displays information about the event. Note that although the source of the events is different, both events were detected by the same action listener: the Action object attached to the components.

4. Choose the top item from the Action State menu.
This disables the "Go left" Action object, which in turn disables its associated menu item and button.

Here is what the user sees when the "Go left" action is disabled:

Using Other Swing Features

Using Other Swing Features

Here's the code that disables the "Go left" action:

boolean selected = ...//true if the action should be enabled;
                      //false, otherwise
leftAction.setEnabled(selected);

After you create components using an Action, you might well need to customize them. For example, you might want to customize the appearance of one of the components by adding or deleting the icon or text. For example, ActionDemo.java has no icons in its menus, and no text in its buttons. Here's the code that accomplishes this:

menuItem = new JMenuItem();
menuItem.setAction(leftAction);
menuItem.setIcon(null); //arbitrarily chose not to use icon in menu
...
button = new JButton();
button.setAction(leftAction);
button.setText(""); //an icon-only button

We chose to create an icon-only button and a text-only menu item from the same action by setting the icon property to null and the text to an empty string. However, if a property of the Action changes, the widget may try to reset the icon and text from the Action again.

The Action API


The following tables list the commonly used Action constructors and methods. The API for using Action objects falls into three categories:
  • Components that Support set/getAction
  • Creating and Using an AbstractAction
  • Action Properties
Components that Support set/getAction

Class Purpose
AbstractButton
JComboBox
JTextField
These components and their subclasses may have an action directly assigned to them via setAction. For further information about components that are often associated with actions, see the sections on tool bar buttons, menu items, common buttons, and text fields. For details on which properties each component takes from the Action, see the API documentation for the relevant class's configurePropertiesFromAction method. Also refer to the buttonActions table.

Creating and Using an AbstractAction

 Constructor or Method  Purpose
AbstractAction()
AbstractAction(String)
AbstractAction(String, Icon)
Create an Action object. Through arguments, you can specify the text and icon to be used in the components that the action is attached to.
void setEnabled(boolean)
boolean isEnabled()
Set or get whether the components the action controls are enabled. Invoking setEnabled(false) disables all the components that the action controls. Similarly, invoking setEnabled(true) enables the action's components.
void putValue(String, Object)
Object getValue(String)
Set or get an object associated with a specified key. Used for setting and getting properties associated with an action.

Action Properties

This table defines the properties that can be set on an action. The second column lists which components automatically use the properties (and what method is specifically called). For example, setting the ACCELERATOR_KEY on an action that is then attached to a menu item, means that JMenuItem.setAccelerator(KeyStroke) is called automatically.

Property Auto-Applied to:
Class
(Method Called)
Purpose
 ACCELERATOR_KEY  JMenuItem
(setAccelerator)
 The KeyStroke to be used as the accelerator for the action. For a discussion of accelerators versus mnemonics.
 ACTION_COMMAND_KEY  AbstractButton, JCheckBox, JRadioButton
(setActionCommand)
 The command string associated with the ActionEvent.
 LONG_DESCRIPTION  None The longer description for the action. Can be used for context-sensitive help.
 MNEMONIC_KEY  AbstractButton, JMenuItem, JCheckBox, JRadioButton
(setMnemonic)
The mnemonic for the action. For a discussion of accelerators versus mnemonics
 NAME  AbstractButton, JMenuItem, JCheckBox, JRadioButton
(setText)
The name of the action. You can set this property when creating the action using the AbstractAction(String) or AbstractAction(String, Icon) constructors.
 SHORT_DESCRIPTION  AbstractButton, JCheckBox, JRadioButton
(setToolTipText)
The short description of the action.
 SMALL_ICON  AbstractButton, JMenuItem
(setIcon)
The icon for the action used in the tool bar or on a button. You can set this property when creating the action using the AbstractAction(name, icon) constructor.

Examples that Use Actions

The following examples use Action objects

ExampleWhere DescribedNotes
ActionDemoThis sectionUses actions to bind buttons and menu items to the same function.
TextComponentDemoText Component FeaturesUses text actions to create menu items for text editing commands, such as cut, copy, and paste, and to bind key strokes to caret movement. Also implements custom AbstractAction subclasses to implement undo and redo

«« Previous
Next »»