Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

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

Practice makes perfect

Experience is often your best defense against Java pitfalls

  • Print
  • Feedback
This month's article focuses on hard-won experience: both my experience and the experiences of this column's readers. Many new Java developers fall into traps simply because they lack familiarity with the language. Through this column and the book Java Pitfalls, I strive to ease the learning curve for Java newbies by sharing the experiences that other developers and I have gained. In this article (my last, as I am now busy writing a second volume to accompany Java Pitfalls), I share with you pitfalls that I and other developers encountered in JLayeredPane, Enumeration, and File.renameTo().

Pitfall 9: Assuming too much from JLayeredPane

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.

  • Print
  • Feedback

Resources