Search code examples
javaswingjpanelawt

Java Swing JLayeredPane not showing CardLayout cards & background image


I'm using Java 17 for a little project. I'm doing the Layout with the AWT CardLayout and the graphical components with Swing.

I want to change my currently active JPanel in Window.java whenever I click on one of the JMenuBarItems inside my Navbar.java class but show my background image from my BackgroundImage class all the time by using a JLayeredPane.

Window.java:

public class Window extends JFrame {
JPanel cards;
JLayeredPane layeredPane;

JPanel openPokédex = new OpenPokédexPanel();

public Window() {
addWindowListener(new WindowAdapter() {
    public void windowClosing (WindowEvent e) {
    dispose();
    }
});

this.setSize(800, 600);

layeredPane = new JLayeredPane();
this.setContentPane(layeredPane);

BackgroundImage background = new BackgroundImage();
layeredPane.add(background, Integer.valueOf(0));

cards = new JPanel(new CardLayout());
layeredPane.add(cards, Integer.valueOf(1));

cards.add(openPokédex, "openPokedex");

Navbar bar = new Navbar(cards, this);
this.setJMenuBar(bar);

this.setVisible(true);
}

public void switchPanel(String panelName) {
    CardLayout cardLayout = (CardLayout) cards.getLayout();
    cardLayout.show(cards, panelName);
    System.out.println(panelName);
}
}

Navbar.java:

public class Navbar extends JMenuBar{
JPanel cards;
Window window;

public Navbar(JPanel cards, Window window) {
this.cards = cards;
this.window = window;

JMenu pokedex = new JMenu("Pokédex");
JMenuItem openPokedex = new JMenuItem("Open");
pokedex.add(openPokedex);

this.add(pokedex);

// Events
openPokedex.addActionListener(e -> window.switchPanel("openPokedex"));
}
}

OpenPokédexPanel.java:

public class OpenPokédexPanel extends JPanel {

OpenPokédexPanel() {
// tried doing transparent background but made no difference
//this.setOpaque(false);
//this.setBackground(new Color(255, 255, 255, 0));

// pretty much irrelevant. just to see if the panel is actually there
JButton pokémonButton = new JButton("Pokémon");
this.add(pokémonButton);
}
}

BackgroundImage.java:

public class BackgroundImage extends JComponent {
BufferedImage image = null;

public BackgroundImage() {
File bgimage = null;

File directory = new File("src/bin/");

if (directory.isDirectory()) {
    File[] files = directory.listFiles();
    
    if (files != null && files.length > 0) {
    Random rng = new Random();
    File randomFile = files[rng.nextInt(files.length)];
    bgimage = randomFile;
    
    } else {
    System.out.println("No images!");
    }
} else {
    System.out.println("Not a directory!");
}

try {
    image = ImageIO.read(bgimage);
} catch (IOException e) {
    e.printStackTrace();
}
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
    g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
}
}

Before trying to combine the BackgroudImage with the CardLayout, the background image worked fine but the JPanels weren't showing up because they apparently spawned behind my background image. I want to have a permanent background with JPanel displaying content on top. At the moment it only shows a white screen when starting the program

EDIT 1

I modified OpenPokédexPanel JPanel class:

public class OpenPokédexPanel extends JPanel {

OpenPokédexPanel() {

this.setOpaque(false); // added this again
//this.setBackground(new Color(255, 255, 255, 0));

JButton pokémonButton = new JButton("Pokémon");
this.add(pokémonButton);
}
}

and I modified the main Window class. I removed the JLayeredPane and added the following code as @camickr suggested:

BackgroundImage background = new BackgroundImage();
this.setLayout(new BorderLayout());
this.setContentPane(background);

cards = new JPanel(new CardLayout());
cards.setOpaque(false);
background.add(cards);

cards.add(openPokédex, "openPokedex");

The background image shows up but the JPanel card openPokédex won't show up as well on click.


Solution

  • If you want to use JLayeredPane this way, then you will need to apply a suitable layout manager to it, for example...

    import java.awt.BorderLayout;
    import java.awt.CardLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JLayeredPane;
    import javax.swing.JPanel;
    import javax.swing.border.EmptyBorder;
    
    public class Main {
        public static void main(String[] args) {
            new Main();
        }
    
        public Main() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new MainPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public interface Navigatable {
            enum View {
                MENU, CONTENT;
            }
            public void show(View view);
        }
    
        public class MainPane extends JPanel implements Navigatable {
    
            private JLayeredPane layeredPane;
    
            private JPanel contentPane;
            private CardLayout cardLayout;
    
            private MenuPane menuPane;
    
            public MainPane() {
                setLayout(new BorderLayout());
    
                layeredPane = new JLayeredPane();
                add(layeredPane);
    
                layeredPane.setLayout(new GridBagLayout());
    
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.weightx = 1;
                gbc.weighty = 1;
                gbc.fill = GridBagConstraints.BOTH;
    
                BackgroundPane backgroundPane = new BackgroundPane();
                layeredPane.add(backgroundPane, gbc);
                layeredPane.setLayer(backgroundPane, Integer.valueOf(0));
    
                cardLayout = new CardLayout();
                contentPane = new JPanel(cardLayout);
                contentPane.setOpaque(false);
    
                layeredPane.add(contentPane, gbc);
                layeredPane.setLayer(contentPane, Integer.valueOf(1));
    
                contentPane.add(new MenuPane(this), Navigatable.View.MENU.name());
                JLabel label = new JLabel("This is the content");
                label.setHorizontalAlignment(JLabel.CENTER);
                contentPane.add(label, Navigatable.View.CONTENT.name());
            }
    
            @Override
            public void show(View view) {
                cardLayout.show(contentPane, view.name());
            }
        }
    
        public class MenuPane extends JPanel {
            public MenuPane(Navigatable navigatable) {
                setOpaque(false);
                setLayout(new GridBagLayout());
                setBorder(new EmptyBorder(32, 32, 32, 32));
    
                JButton btn = new JButton("Show me the content");
                add(btn);
    
                btn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        navigatable.show(Navigatable.View.CONTENT);
                    }
                });
            }       
        }
    
        public class BackgroundPane extends JPanel {
    
            private BufferedImage bgImage;
    
            public BackgroundPane() {
                try {
                    bgImage = ImageIO.read(getClass().getResource("/images/Mando01-50.png"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
    
            @Override
            public Dimension getPreferredSize() {
                if (bgImage != null) {
                    return new Dimension(bgImage.getWidth(), bgImage.getHeight());
                }
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (bgImage == null) {
                    return;
                }
                int x = (getWidth() - bgImage.getWidth()) / 2;
                int y = (getHeight() - bgImage.getHeight()) / 2;
                g.drawImage(bgImage, x, y, this);
            }
        }
    }
    

    But, a generally better solution would probably be to make use of the background pane directly, for example...

    import java.awt.BorderLayout;
    import java.awt.CardLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.GridBagLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.border.EmptyBorder;
    
    public class Main {
        public static void main(String[] args) {
            new Main();
        }
    
        public Main() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new MainPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public interface Navigatable {
            enum View {
                MENU, CONTENT;
            }
            public void show(View view);
        }
    
        public class MainPane extends JPanel implements Navigatable {
    
            private BackgroundPane backgroundPane;
            private CardLayout cardLayout;
    
            private MenuPane menuPane;
    
            public MainPane() {
                setLayout(new BorderLayout());
    
                cardLayout = new CardLayout();
                backgroundPane = new BackgroundPane();
                backgroundPane.setLayout(cardLayout);
                add(backgroundPane);
    
                backgroundPane.add(new MenuPane(this), Navigatable.View.MENU.name());
                JLabel label = new JLabel("This is the content");
                label.setHorizontalAlignment(JLabel.CENTER);
                backgroundPane.add(label, Navigatable.View.CONTENT.name());
            }
    
            @Override
            public void show(View view) {
                cardLayout.show(backgroundPane, view.name());
            }
        }
    
        public class MenuPane extends JPanel {
            public MenuPane(Navigatable navigatable) {
                setOpaque(false);
                setLayout(new GridBagLayout());
                setBorder(new EmptyBorder(32, 32, 32, 32));
    
                JButton btn = new JButton("Show me the content");
                add(btn);
    
                btn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        navigatable.show(Navigatable.View.CONTENT);
                    }
                });
            }       
        }
    
        public class BackgroundPane extends JPanel {
    
            private BufferedImage bgImage;
    
            public BackgroundPane() {
                try {
                    bgImage = ImageIO.read(getClass().getResource("/images/Mando01-50.png"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
    
            @Override
            public Dimension getPreferredSize() {
                if (bgImage != null) {
                    return new Dimension(bgImage.getWidth(), bgImage.getHeight());
                }
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (bgImage == null) {
                    return;
                }
                int x = (getWidth() - bgImage.getWidth()) / 2;
                int y = (getHeight() - bgImage.getHeight()) / 2;
                g.drawImage(bgImage, x, y, this);
            }
        }
    }