Library Zone Articles
External Articles
Byte Size

Discovery Zone Catalogue
Diary
Links
Bookstore
Interactive Zone Ask the Gurus
Discussion Groups
Newsletters
Feedback
Etc Cartoons
Humour
COMpetition
Advertising ASP Web Ring ASP Web Ring
VisualStudioWire.com - Get your VS.NET news here
The Developer's Resource & Community Site
COM XML ASP Java & Misc. NEW: VS.NET
International This Week Forums Author Central Find a Job
Buy the book: Java Foundation Classes in a Nutshell

Java Foundation Classes in a Nutshell: Swing Programming Topics, page 2

Reproduced with kind permision of O'Reilly & Associates: www.oreilly.com

3.13 Double-Buffering

Double-buffering is the process of drawing graphics into an off-screen image buffer and then copying the contents of the buffer to the screen all at once. For complex graphics, using double-buffering can reduce flickering. Swing automatically supports double-buffering for all of its components. To enable it, simply call the setDoubleBuffered() method (inherited from JComponent) to set the doubleBuffered property to true for any components that should use double-buffered drawing.

Remember that double-buffering is memory intensive. Its use is typically only justified for components that are repainted very frequently or have particularly complex graphics to display. Note, however, that if a container uses double-buffering, any double-buffered children it has share the off-screen buffer of the container, so the required off-screen buffer is never larger than the on-screen size of the application.

3.14 The Box Container

Chapter 2, Swing and AWT Architecture, discussed the general task of arranging components within containers and listed the layout managers provided by AWT and Swing. This section describes a commonly used Swing layout management technique in detail. The easiest way to create complex arrangements of Swing components is often with the javax.swing.Box container.[1] Box arranges its components into a single row or a single column. You can then use nested Box containers to create a two-dimensional arrangement of components.

[1] For some reason, Box does not begin with the letter J as other Swing components and containers do. Nevertheless, it is a very useful and commonly used container.

The Box container uses the BoxLayout layout manager, but this layout manager is automatically assigned, so you never need to work with it explicitly. The easiest way to create a Box is with the static Box.createHorizontalBox() or Box.createVerticalBox() method. Once you have created a Box, simply add children to it. They will be arranged from left to right or from top to bottom.

The unique power of the Box actually comes from an inner class called Box.Filler. This class is a simple component that has no appearance; it exists simply to insert blank space in a layout and to affect the resize behavior of the layout. You do not create Box.Filler objects directly. Instead, you create them using the following static methods of Box:

Box.createHorizontalStrut(int 

width)
Box.createVerticalStrut(int 

height)
Box.createHorizontalGlue()
Box.createVerticalGlue()

If you are arranging a row of components, you can call createHorizontalStrut() to insert a fixed number of pixels of blank horizontal space. For a column of components, use createVerticalStrut() to insert a blank vertical space.

The glue methods are different. They insert stretchy horizontal or vertical space into a row or column. By default, the space is zero pixels wide or zero pixels high. But, if the row or column is stretched so that it becomes wider or higher than its default size, these glue components stretch to take up that extra space. For example, say you fill a row with some horizontal glue, a JButton component, and some more horizontal glue. Now, no matter how wide the row becomes, the JButton is always centered in it. This is because the two glue components (and possibly the JButton) grow equally to take up the extra space. On the other hand, if the row consists of only one glue component followed by a JButton, the JButton always appears right justified in the row, since the glue component grows to take up all the space to the left of the button.

As another example, consider a Box used in a dialog to hold a row of OK, Cancel, and Help buttons. Without any glue, the buttons are resized to fill up the entire row, with no extra space between them. If we intersperse the three buttons with four glue components, however, the buttons are always nicely spaced out and the buttons and the spaces between them grow proportionally as the dialog box becomes wider.

3.14.1 Minimum, Preferred, and Maximum Sizes

In order to fully understand the behavior of the Box container and its glue, it is important to understand that Swing components can have a minimum size, a preferred size, and a maximum size. Many components have a natural size. For example, with a JButton, the natural size is the space required to accommodate the button text and/or Icon, plus the space required for the button border. By default, a JButton reports its natural size as its minimum size and as its preferred size. When asked for its maximum size, a JButton returns very large integers, indicating that it can grow to become arbitrarily wide and arbitrarily tall.

Swing components (but not AWT components) allow you to specify their minimum, preferred, and maximum sizes. For example, if you do not want to allow a JButton to become arbitrarily large as its container grows larger, you can set a maximum size for it by calling setMaximumSize(). Setting a preferred size for a JButton is an uncommon thing to do, as JButton has a perfectly good natural size. But some components, such as JScrollPane objects, do not have a natural size. For components like these, it is usually important that you establish a default size with setPreferredSize(). If you want to prevent a JScrollPane or similar component from becoming arbitrarily small or arbitrarily large, you should also call setMinimumSize() and setMaximumSize().

Now that you understand the concepts of minimum, preferred, and maximum sizes, we can return to the Box container and its struts and glue. Both struts and glue are instances of the Box.Filler component. When you create a Box.Filler, you are actually specifying minimum, preferred, and maximum sizes for the component. A horizontal strut is simply a Box.Filler with its minimum, preferred, and maximum width set to the number of pixels you specify. A vertical strut has a fixed minimum, preferred, and maximum height.

Horizontal glue has a minimum and preferred width of zero, but a very large maximum width. This means that the glue takes up no space by default but grows as necessary to fill up extra space. Vertical glue does the same thing in the other dimension. In order to understand glue, it is also important to understand how the Box container distributes excess space to its children. If a horizontal Box becomes wider, the extra width is allocated among the children based on their maximum widths. Children with larger maximums are given a proportionally larger amount of the extra space. When you intersperse JButton objects with glue, all the components have effectively infinite maximum widths, so all grow by equal amounts. Suppose, instead, that you restricted the sizes of your buttons like this:

okayButton.setMaximumSize(okayButton.getPreferredSize());
cancelButton.setMaximumSize(cancelButton.getPreferredSize());
helpButton.setMaximumSize(helpButton.getPreferredSize());
In this case, the buttons are already at their maximum sizes, so no extra space is allocated to them. Now the glue between the buttons gets all the extra space.

I just said that glue components have a preferred size of zero. With regard to the example of three buttons interspersed with four glue components, this means that when the row of buttons is displayed at its default size, the buttons bump into one another and appear awkwardly crowded. To remedy this, you might place horizontal struts and horizontal glue between the buttons. In this case, the struts provide the default and minimum spacing, while the glue components make the spacing grow. There is a more efficient way to do this, however. You can explicitly create Box.Filler components that combine the nonzero default size of a strut with the infinite maximum size of a glue object. You can create such a filler object as follows:

Dimension fixedwidth = new Dimension(15, 0);
Dimension infinitewidth = new Dimension(Short.MAX_VALUE, 0);
Box.Filler filler = new Box.Filler(fixedwidth, fixedwidth, infinitewidth);

3.14.2 The Other Dimension

So far, our discussion of the Box container has covered only how components are arranged horizontally in a horizontal box or vertically in a vertical box. What does Box do in the other dimension? When laying out components in a row, the Box makes the row as tall as the tallest component and then attempts to make all the components as tall as the row. Similarly, when it lays out components in a column, Box tries to make all components as wide as the widest component.

As we've discussed, however, components can have a maximum size. If a row becomes taller than a component's maximum height or a column becomes wider than a component's maximum width, the Box must decide how to position the component with respect to the others in the row or column. For a column, the component can be left, center, or right justified or positioned anywhere in between. A component in a row can be aligned along the top or bottom of the row or placed somewhere in between.

A Box positions such a component based on its alignmentX or alignmentY property. Each is a float property that should have a value between 0.0 and 1.0. The default for both is 0.5. When a component needs to be positioned horizontally in a column, the Box uses the alignmentX property. A value of 0.0 means the component is left justified, 1.0 means the component is right justified, and 0.5 means the component is centered. Other values position the component appropriately between these positions. When a Box needs to position a component vertically in a row, it uses the component's alignmentY property to place the component in the vertical plane in an analogous way.

3.15 Simple Dialogs

GUIs often use dialog boxes to handle simple interactions with the user. javax.swing.JOptionPane is a Swing component that is designed to serve as a highly configurable body of a dialog box. Instead of using the JOptionPane directly, however, most Swing programs use one or more of the many static methods defined by JOptionPane. These methods make it quite easy to implement simple dialog-based interactions.

If you take a look at the API for JOptionPane, you'll see that the class defines a group of static methods whose names begin with show and another whose names begin with showInternal. The show methods display simple dialog boxes within JDialog windows, while the showInternal methods display the same dialog boxes inside JInternalFrame windows. These static methods are further broken down by the type of dialog they display. There are several versions of showMessageDialog(), showConfirmDialog(), and showInputDialog(), as well as showInternal versions of the same methods. We'll consider these three types of dialogs - message, confirm, and input - in the sections that follow.

3.15.1 Message Dialogs

Message dialogs are used to display important information to users in a way that is difficult or impossible for them to miss. For example, you might use a message dialog to tell the user that a requested file was not found. To display this message with a JOptionPane, you can use code like this:

JOptionPane.showMessageDialog(mainpanel, "The file you requested, " + 
                              filename + ", was not found. Please try again");
This code produces the dialog shown in Figure 3.4. The dialog remains visible until the user dismisses it by clicking OK.

Figure 3.4: A JOptionPane message dialog

The first argument to showMessageDialog() is the component over which the dialog is to appear. You typically specify the main window or panel of your application. If you specify null, then the dialog will simply be centered on the screen. The second argument is obviously the message to be displayed. If you look at the API again, however, you'll notice that the message argument to this and other JOptionPane methods is defined as an Object, not a String. This means that you are not limited to textual messages. If you pass a Component or an Icon, the JOptionPane displays it as the message. If you pass some other kind of object, JOptionPane attempts to convert it to a string by calling its toString() method. You can even pass an array of objects as the message argument. When you pass more than one object, the objects are displayed top to bottom in the resulting dialog. So, to display a multiline message, for example, you can just pass in an array of String objects, instead of a single long String.

The showMessageDialog() function has variants that take more arguments. The title argument specifies the text to appear in the titlebar of the dialog. The messageType argument specifies the general type of the message. Legal values are the JOptionPane constants that end with _MESSAGE. The values you are most likely to use are INFORMATION_MESSAGE, WARNING_MESSAGE, and ERROR_MESSAGE. Specifying a message type implicitly specifies the icon that appears in the dialog box. If you don't like the default icons, however, there is a version of showMessageDialog() that lets you specify your own icon to display.

3.15.2 Confirm Dialogs

You can use JOptionPane.showConfirmDialog() or JOptionPane.showInternalConfirmDialog() when you want to ask the user a simple question that requires a Yes or No (or perhaps Cancel) answer. For example, you can use one of these methods to present the dialog shown in Figure 3.5.

Figure 3.5: A JOptionPane confirm dialog

The arguments to showConfirmDialog() are much like the arguments to showMessageDialog(), with the addition of the optionType argument. This argument specifies the set of buttons that appears at the bottom of the dialog. Legal values are OK_CANCEL_OPTION, YES_NO_OPTION, and YES_NO_CANCEL_OPTION.

A confirm dialog asks the user a question. The return value of showOptionDialog() or showInternalOptionDialog() is an integer that represents the user's answer in terms of the button the user clicked to dismiss the dialog. The possible values are OK_OPTION, YES_OPTION, NO_OPTION, CANCEL_OPTION, and CLOSED_OPTION. This last value is returned if the user did not click any of the dialog buttons but instead dismissed the dialog by closing the window. Here is some simple code that asks a question with a confirm dialog (note the use of a string array for the message argument):

int response = JOptionPane.showConfirmDialog(mainpanel, new String[] {
       /* first line of the message */       "There are unsaved files.",
       /* second line of message    */       "Save them before quitting?"},
       /* dialog title              */       "Save Before Quitting?",
       /* what buttons to display   */       JOptionPane.YES_NO_CANCEL_OPTION,
       /* icon type to display      */       JOptionPane.WARNING_MESSAGE);
switch(response) {
  case JOptionPane.YES_OPTION:     saveAndQuit();
  case JOptionPane.NO_OPTION:      quitWithoutSaving();
  case JOptionPane.CANCEL_OPTION:
  case JOptionPane.CLOSED_OPTION:  break;  // Don't quit!
}

3.15.3 Input Dialogs

The showInputDialog() and showInternalInputDialog() methods are designed to ask for input that is more complex than a yes-or-no answer. The simple versions of showInputDialog() support asking a question like "What is your name?" and letting the user type a response in a text input area:

String name = JOptionPane.showInputDialog(frame, "What is your name?");

The more complex version of this method allows the user to select an object from a list or pull-down menu of predefined options.

The arguments to showInputDialog() are quite similar to those passed to showMessageDialog() and showConfirmDialog(). To display a list of options to the user, use the seven-argument version of the method and pass in an array of choices and the default choice to display. For example:

String response = (String) JOptionPane.showInputDialog(
		      contentpane,                       // parent
		     "Who is your favorite chipmunk?",   // message
		     "Pick a Chipmunk",                  // dialog title
		      JOptionPane.QUESTION_MESSAGE,      // icon type
		      null,                              // no explicit icon
		      new String[] {                     // choices
			 "Alvin", "Simon", "Theodore" 
		      },  
		      "Alvin");                          // default choice

3.16 JFileChooser

javax.swing.JFileChooser is a specialized component that allows the user to browse the filesystem and select a file. The easiest way to use it is with the showOpenDialog() and showSaveDialog() methods. These methods differ only in the text that appears in the "Okay" button. You can also call the showDialog() method and specify your own text for that button. Each of these methods returns an integer status code that specifies how the user dismissed the dialog. If the return value is APPROVE_OPTION, the user actually selected a file, which you can obtain with the getSelectedFile() method. For example:

public void saveAs() {
  JFileChooser chooser = new JFileChooser();
  int result = chooser.showSaveDialog(mainpane);
  if (result == JFileChooser.APPROVE_OPTION)
    save(chooser.getSelectedFile());
}

Note that showSaveDialog() and showOpenDialog() are instance methods, not static methods like those used with JOptionPane. This means that you can customize the dialog by setting properties on your JFileChooser object. You may be interested in setting the currentDirectory and fileSelectionMode properties before you display a JFileChooser. fileSelectionMode can be set to FILES_ONLY, DIRECTORIES_ONLY, or FILES_AND_DIRECTORIES. Once you create a JFileChooser for an application, you may want to reuse it, rather than creating a new one each time you need one. If you do so, the JFileChooser automatically remembers the currentDirectory most recently selected by the user.

3.16.1 Using File Filters

The javax.swing.filechooser package defines auxiliary classes that are used by JFileChooser. One of the most important of these is FileFilter. The abstract javax.swing.filechooser.FileFilter class is much like the java.io.FileFilter interface. Each defines an accept() method that is passed File objects and returns true for each file that should be displayed. The FileFilter class used by JFileChooser has an additional getDescription() method that returns a string that names the types of files accepted by the filter. For example, you might define a FileFilter subclass that accepts files with names ending in .htm or .html and returns a description of "HTML Files."

When you create a JFileChooser, you can specify the FileFilter it is to use with setFileFilter(). Alternately, you can specify an array of FileFilter objects with setChoosableFileFilters(). In this case, JFileChooser displays the descriptions of the filters and allows the user to choose one.

3.16.2 Customizing JFileChooser

The behavior of a JFileChooser can be customized by providing your own implementation of FileView and FileSystemView. Both of these abstract classes are defined in the javax.swing.filechooser class. FileView defines methods that affect the way individual files are displayed by the JFileChooser, while FileSystemView defines methods that enable the JFileChooser to handle operating-system dependencies in the filesystem. FileSystemView understands the notion of hidden files, and it can return a complete list of filesystem roots, a capability that was lacking from the basic java.io.File class prior to Java 1.2. The default FileView and FileSystemView classes provided by JFileChooser are perfectly adequate for most purposes, so you typically don't have to implement these classes yourself.

It is also possible to customize a JFileChooser by providing an accessory component. If you pass a JComponent to the setAccessory() method of JFileChooser, the Swing component you specify is displayed in the file chooser dialog box. A common use of a file chooser accessory is as a file preview component. In order to provide a preview of the currently selected file, the accessory must know what the currently selected file is. It can get this information by implementing the PropertyChangeListener interface and listening for changes to the selectedFile property. In order for this to work, you have to pass the accessory object to the addPropertyChangeListener() method of the JFileChooser, of course.

Next Page | Previous Page


iDevJobs.com - Jobs for Professional Developers

Contribute to IDR:

To contribute an article to IDR, a click here.

To contact us at IDevResource.com, use our feedback form, or email us.

To comment on the site contact our webmaster.

Promoted by CyberSavvy UK - website promotion experts

All content © Copyright 2000 IDevResource.com, Disclaimer notice

Code Project

Join the Developers Webring



Learn C#

WTL Introduction