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 JMenuBarItem
s 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 JPanel
s 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.
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);
}
}
}