Question

To put it simple, there's a simple java swing app that consists of JFrame with some components in it. One of the components is a JPanel that is meant to be replaced by another JPanel on user action.

So, what's the correct way of doing such a thing? I've tried

panel = new CustomJPanelWithComponentsOnIt();
parentFrameJPanelBelongsTo.pack();

but this won't work. What would you suggest?

Was it helpful?

Solution

Your use case, seems perfect for CardLayout.

In card layout you can add multiple panels in the same place, but then show or hide, one panel at a time.

OTHER TIPS

1) Setting the first Panel:

JFrame frame=new JFrame();
frame.getContentPane().add(new JPanel());

2)Replacing the panel:

frame.getContentPane().removeAll();
frame.getContentPane().add(new JPanel());

Also notice that you must do this in the Event's Thread, to ensure this use the SwingUtilities.invokeLater or the SwingWorker

frame.setContentPane(newContents());
frame.revalidate(); // frame.pack() if you want to resize.

Remember, Java use 'copy reference by value' argument passing. So changing a variable wont change copies of the reference passed to other methods.

Also note JFrame is very confusing in the name of usability. Adding a component or setting a layout (usually) performs the operation on the content pane. Oddly enough, getting the layout really does give you the frame's layout manager.

Hope this piece of code give you an idea of changing jPanels inside a JFrame.

public class PanelTest extends JFrame {

        Container contentPane;

        public PanelTest()  {
           super("Changing JPanel inside a JFrame");
           contentPane=getContentPane();
        }

        public void createChangePanel() {
           contentPane.removeAll();
           JPanel newPanel=new JPanel();
           contentPane.add(newPanel);
           System.out.println("new panel created");//for debugging purposes
           validate();
           setVisible(true);
        }
}

On the user action:

// you have to do something along the lines of

myJFrame.getContentPane().removeAll()
myJFrame.getContentPane().invalidate()

myJFrame.getContentPane().add(newContentPanel)
myJFrame.getContentPane().revalidate()

Then you can resize your wndow as needed.

It all depends on how its going to be used. If you will want to switch back and forth between these two panels then use a CardLayout. If you are only switching from the first to the second once and (and not going back) then I would use telcontars suggestion and just replace it. Though if the JPanel isn't the only thing in your frame I would use remove(java.awt.Component) instead of removeAll.

If you are somewhere in between these two cases its basically a time-space tradeoff. The CardLayout will save you time but take up more memory by having to keep this whole other panel in memory at all times. But if you just replace the panel when needed and construct it on demand, you don't have to keep that meory around but it takes more time to switch.

Also you can try a JTabbedPane to use tabs instead (its even easier than CardLayout because it handles the showing/hiding automitically)

The other individuals answered the question. I want to suggest you use a JTabbedPane instead of replacing content. As a general rule, it is bad to have visual elements of your application disappear or be replaced by other content. Certainly there are exceptions to every rule, and only you and your user community can decide the best approach.

Problem: My component does not appear after I have added it to the container.

You need to invoke revalidate and repaint after adding a component before it will show up in your container.

Source: http://docs.oracle.com/javase/tutorial/uiswing/layout/problems.html

I was having exactly the same problem!! Increadible!! The solution I found was:

  1. Adding all the components (JPanels) to the container;
  2. Using the setVisible(false) method to all of them;
  3. On user action, setting setVisible(true) to the panel I wanted to show.
// Hiding all components (JPanels) added to a container (ex: another JPanel)
for (Component component : this.container.getComponents()) {
      component.setVisible(false);
}
// Showing only the selected JPanel, the one user wants to see
panel.setVisible(true);

No revalidate(), no validate(), no CardLayout needed.

The layout.replace() answer only exists/works on the GroupLayout Manager.

Other LayoutManagers (CardLayout, BoxLayout etc) do NOT support this feature, but require you to first RemoveLayoutComponent( and then AddLayoutComponent( back again. :-) [Just setting the record straight]

I suggest you to add both panel at frame creation, then change the visible panel by calling setVisible(true/false) on both. When calling setVisible, the parent will be notified and asked to repaint itself.

class Frame1 extends javax.swing.JFrame {

    remove(previouspanel); //or getContentPane().removeAll();

    add(newpanel); //or setContentPane(newpanel);

    invalidate(); validate(); // or ((JComponent) getContentPane()).revalidate();

    repaint(); //DO NOT FORGET REPAINT

}

Sometimes you can do the work without using the revalidation and sometimes without using the repaint.My advise use both.

Just call the method pack() after setting the ContentPane, (java 1.7, maybe older) like this:

JFrame frame = new JFrame();  
JPanel panel1 = new JPanel(); 
JPanel panel2 = new JPanel(); 
....
frame.setContentPane(panel1);
frame.pack();
...

frame.setContentPane(panel2);
frame.pack();
...

you can use the replace method of a layout:

layout.replace(existingComponent, newComponent)

the existing and new components can be JPanels, too.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top