|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
JLayeredPane, Enumeration, and File.renameTo().While working on the jXUL project (an open source effort to integrate XUL (Extensible User Interface Language) with Java) for the book Essential XUL Programming, I ported a Pac-Man arcade game clone called Pagman to a Java-based XulRunner platform. The XulRunner Java class executes XUL applications; it's similar to the JDK's AppletRunner. Figure 1 provides a screenshot of the current version of the Pagman port, which successfully allows the ghost sprites to
move on a JLayeredPane's top layer. The sprites move over the background images, which exist in a layer beneath. (Many thanks to my coauthor Kevin
Smith, who worked through these pitfalls with me to bring Pagman to fruition.)

Figure 1. Pagman screen in XulRunner
Instead of examining the pitfall encountered in the XulRunner code, which is rather large, we will examine a simpler example that demonstrates the problem. Those interested in the Pagman
code can download it from the jXUL Website.
Our simple BadLayeredPane example attempts to create a frame that has a colored panel in a background layer and a button in a foreground layer with
a JLayeredPane:
Listing 9.1. BadLayeredPane.java
package com.javaworld.jpitfalls.article5;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class BadLayeredPane extends JFrame
{
public BadLayeredPane()
{
// Error 1: using the root layered pane
JLayeredPane lp = getLayeredPane();
// Set the size of this pane
lp.setPreferredSize(new Dimension(100,100));
// Add a colored Panel
JPanel jpnl = new JPanel();
jpnl.setSize(100,100);
jpnl.setOpaque(true);
jpnl.setBackground(Color.red);
// Error 2: these MUST be of type integer.
lp.add(jpnl, 2);
// Put a Button on top
Button b = new Button("Hi!");
// Error 3: adding button wrong
lp.add(b, 1);
}
public static void main(String [] args)
{
JFrame frame = new BadLayeredPane();
frame.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
}
When Listing 9.1 runs, it produces the screen in Figure 2.
![]()
Figure 2. Run of BadLayeredPane
Our JLayeredPane isn't just working improperly; it also has no size! We must first work through the size problem before we can approach the
heart of our pitfall.
Listing 9.1 features three errors (called out in the comments); I'll tackle the first two now and address the third later.
First, the JLayeredPane that is part of the JFrame's JRootPane causes our size problem. When you examine JRootPane's source code, you see that the JRootPane's RootLayout does not use the JLayeredPane to calculate its size; JLayeredPane only calculates the size of the content pane and the menu bar. Second, when adding components to our JLayeredPane, we use integers instead of Integer objects.
With this knowledge, let's examine our second attempt at displaying our two simple layers:
Listing 9.2. BadLayeredPane2.java
package com.javaworld.jpitfalls.article5;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class BadLayeredPane2 extends JFrame
{
public BadLayeredPane2()
{
// Fix 1: Create a JLayeredPane
JLayeredPane lp = new JLayeredPane();
// Set the size of this pane
lp.setPreferredSize(new Dimension(100,100));
// Add a colored Panel
JPanel jpnl = new JPanel();
jpnl.setSize(100,100);
jpnl.setOpaque(true);
jpnl.setBackground(Color.red);
// Fix 2: use Integer objects
lp.add(jpnl, new Integer(2));
// Put a Button on top
Button b = new Button("Hi!");
lp.add(b, new Integer(1));
// Part of Fix 1
getContentPane().add(lp);
}
public static void main(String [] args)
{
JFrame frame = new BadLayeredPane2();
frame.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
}
We'll first study the fixes applied and then the results. There are two fixes in Listing 9.2 (called out in the comments).
First, we create a new JLayeredPane, which we add to the ContentPane. The RootLayout manager uses the ContentPane to calculate the frame's size, so now the JFrame is packed properly. Second, we correctly add components to the JLayeredPane using an Integer object to specify the layer. Figure 3 shows the result of these fixes.

Figure 3. Run of BadLayeredPane2
Figure 3 clearly demonstrates that we have not yet accomplished our goal. Though the colored panel displays, the button fails
to appear on the layer above the panel. Why? Because we assume that we add components to a JLayeredPane in the same way that we add components to Frames and Panels. This assumption is our third error and the JLayeredPane pitfall. Unlike Frame and Panel, the JLayeredPane lacks a default LayoutManager; thus, the components have no sizes or positions provided for them by default. Instead, you must explicitly set the size
and position of a component before adding it to the JLayeredPane, which Fix 1 achieves in Listing 9.3:
Listing 9.3. GoodLayeredPane.java
package com.javaworld.jpitfalls.article5;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class GoodLayeredPane extends JFrame
{
public GoodLayeredPane()
{
JLayeredPane lp = new JLayeredPane();
// Set the size of this pane
lp.setPreferredSize(new Dimension(100,100));
// Add a colored Panel
JPanel jpnl = new JPanel();
jpnl.setSize(100,100);
jpnl.setOpaque(true);
jpnl.setBackground(Color.red);
lp.add(jpnl, new Integer(1));
// Put a Button on top
Button b = new Button("Hi!");
// Fix 1: set the size and position
b.setBounds(10,10, 80, 40);
lp.add(b, new Integer(2));
getContentPane().add(lp);
}
public static void main(String [] args)
{
JFrame frame = new GoodLayeredPane();
frame.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
}
When run, Listing 9.3 produces the correct result, shown in Figure 4.