Why does the first panel added to a frame disappear?
Below is an example of adding two panels to a frame. Only one panel (the 2nd, red panel) appears.
Why does the first panel disappear?
import java.awt.Color;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame() {
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(getColoredPanel(Color.GREEN));
f.add(getColoredPanel(Color.RED));
f.pack();
f.setVisible(true);
}
private JPanel getColoredPanel(Color color) {
JPanel p = new JPanel();
p.setBackground(color);
p.setBorder(new EmptyBorder(20, 150, 20, 150));
return p;
}
public static void main(String[] args) {
Runnable r = DisappearingPanelInFrame::new;
SwingUtilities.invokeLater(r);
}
}
- The default layout of a
JFrame
(or more specifically in this case, the content pane of the frame) is aBorderLayout
. - When adding a component to a
BordeLayout
with no constraint, the Swing API will put the component in theCENTER
. - A
BorderLayout
can contain exactly one component in each of the 5 layout constraints. - When a second component is added to the same (in this case
CENTER
) constraint of aBorderLayout
, this implementation of Java will display the last component added.
As to what would be a better approach depends on the specific needs of the user interface.
When a second component is added to the same (in this case CENTER) constraint of a BorderLayout, this implementation of Java will display the last component added.
Not strictly true.
The BorderLayout
will only reset the bounds
(ie size and location) of the last component added to a specific constraint location. This is different from other layout managers in that they will reset the bounds of all components in the container.
In the example code the red panel was the "active" panel at the time the frame was validated by using the pack() method and therefore only its bound were set and therefore only it was painted.
For a demonstration of this process run the example below using the following steps:
- Click on the "Add Panel in Center" button, nothing appears to happen even though the blue panel was added to the center.
- Move the mouse over the red panel and the buttons will appear because the mouse rollover logic will cause the buttons to be repainted.
- Now increase the frame width and the blue panel will appear under the red panel.
The code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame()
{
JButton button = new JButton ("Add Panel In Center");
button.addActionListener( new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
JPanel blue = new JPanel();
blue.setBackground( Color.BLUE );
blue.add( new JButton("Button 1") );
blue.add( new JButton("Button 2") );
Component c = (Component)e.getSource();
Window window = SwingUtilities.windowForComponent(c);
window.add(blue );
window.revalidate();
window.repaint();
}
});
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(new ColoredPanel(Color.GREEN));
//f.pack();
f.add(new ColoredPanel(Color.RED));
f.add(button, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
new DisappearingPanelInFrame();
}
};
SwingUtilities.invokeLater(r);
}
}
class ColoredPanel extends JPanel {
ColoredPanel(Color color) {
setBackground(color);
setBorder(new EmptyBorder(20, 150, 20, 150));
}
}
When the blue panel is added to the BorderLayout and when the revalidate()
is invoked the bounds of the blue panel are set.
However, because of the way Swing does ZOrder painting the blue panel is painted first and then the red panel is painted on top of the blue panel. The green panel still has a size of (0, 0) since it was never the "active" panel in the BorderLayout.CENTER
when the frame was initially validated with the pack() method.
When the frame is resized, the blue panel being the "active" panel in the BorderLayout.CENTER
, has its bounds adjusted, so it will now fill the extra space in the frame.
Now for another test:
-
pack()
the frame after adding the green panel to the frame. - run the code and increase the width of the frame and the red and green frame will appear
- then click the button and increase the width and now all 3 panels will appear
Bottom line is still the same:
Don't try to add multiple panels to the same constraint of a BorderLayout. If you do, then make sure you remove the previous panel or you have the potential for unexpected results.