Sun Java Solaris Communities My SDN Account Join SDN
 
Article

CardPanel - An Alternative to CardLayout

 

CardPanel - An Alternative to CardLayout

   by Hans Muller         


Last week I spent a few days writing an application GUI that required support for displaying a stack of panels, all the same size, where only one panel was visible at a time. When the number of panels is small and fixed, a JTabbedPane is often used. In my application the number of panels varied dynamically between one and twenty-five, which made it unsuitable for a tabbed pane. In addition, the tabbed pane displays the name of each panel to the user, and this wasn't desirable for my GUI.

I decided to use a container with CardLayout instead. CardLayout is an AWT layout manager that arranges an arbitrary number of optionally named components in a stack that's as big as the largest component. It provides methods for restacking a component to the top, thus making it visible, either by name or by relative position in the stack. For example, CardLayout.next() moves the component below the one that's currently visible to the top, thus making it the visible component.

Although CardLayout was designed specifically for the problem I was addressing, I wasn't entirely happy with it. I found it inconvenient to use because, to restack, I needed to cast the container that used CardLayout – a JPanel in my application. For example to show the card named "myCard" in a JPanel with a CardLayout you would write:

    ((CardLayout)(myJPanel.getLayout())).show(myJPanel, "myCard");

Although it's easy to work around this syntactic inconvenience, a more subtle problem lurks below the surface. All of the CardLayout display operations, such as CardLayout's show() call validate directly to ensure that the newly visible component or "card" is resized to fill the CardLayout container. This is very unusual for a layout manager; even though many of the AWT and Swing layout managers provide an API for configuring their properties, CardLayout is the only one that calls validate directly.

Swing components support automatic, batched validation by calling revalidate whenever a change is made that may require a containers size, or the layout of its children, to change. The Swing revalidate method is like repaint in the sense that validate requests are compacted and efficiently handled en-masse at the end of each event-dispatching cycle. In a Swing application a direct call to validate is inefficient, and it forces the application to update the bounds of invalid components piecemeal and sometimes redundantly.

To remedy these problems I wrote a simple JPanel subclass called CardPanel. CardPanel contains its own private CardLayout implementation that uses revalidate as appropriate. It also exposes CardLayout's methods so there is no need to cast; for example, CardLayout.show(cardName) is exposed as CardPanel.showCard(cardName). The new class is intended to support a layout with a modest number of cards, on the order of 100 or less. A card's name is the component name set when the component is added to the CardPanel; it can be retrieved with java.awt.Component.getName():
    myCardPanel.add(myChild, "MyChildName");
    myChild.getName() => "MyChildName"

The CardLayout class maintains a Hashtable internally that maps component names to the components themselves. Since most CardLayouts, and notably my application, contain a small number of cards, the added expense of a hashtable wasn't worth the performance gained by avoiding a linear search when a component is looked up by name. So CardPanel takes advantage of the otherwise underutilized Component name property and lives with the cost of scanning through the list of children to find one by name.

As with CardLayout, the first child added to a CardPanel will be visible to start with, and only one child is visible at a time. The showCard method accepts either a child's name or the child itself:

     myCardPanel.showCard("MyChildName");
     myCardPanel.showCard(myChild);

The CardPanel class doesn't support the vgap and hgap CardLayout properties since one can just add a Border to the panel instead; see JComponent.setBorder() . All of the other CardLayout methods are supported, with the names lengthened a little for the sake of clarity. For example, CardLayout.next() becomes CardPanel.showNextCard().

You can download the source code for CardPanel here.