CardLayout.show() funktioniert teilweise nicht

Faberix

Mitglied
Hallo,

Ich programmiere gerade ein Jump and Run und benutze das CardLayout für die verschiedenen Bildschirme. Ich möchte wenn ein Level geladen wird einen Ladebildschirm einblenden, was aber komischerweise in meinem Listener nicht funktioniert, am Anfang, wenn das Hauptmenü geladen wird allerdings schon.

Die Main-Klasse:

Java:
package ch.faberix.jumpandrun;

import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeListener;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;


public class Main extends JPanel implements ActionListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	
	public static LevelIcon LEVEL_1;
	public static LevelIcon LEVEL_2;
	
	
	public static final String SCREEN_MAIN_MENU = "0";
	public static final String SCREEN_LEVEL_MENU = "1";
	public static final String SCREEN_WAIT = "2";
	public static final String SCREEN_RUNNING_GAME = "3";
	
	
	public String CurrentScreen = "2";
	public static Component currentScreenComponent;
	public static final JPanel levelMenu = new LevelMenu();

	private JButton start;
	public static JFrame f;
	public JFrame frame;
	
	public Game4 game4;
	public Game game;
	
	public static Image background;
	
	private static int width;
	private static int height;
	
	private static CardLayout cardLayout = new CardLayout();
	public LoadingScreen loadingScreen = null;
			
	private static Main Instance;
	private static Main.LevelButtonListener LButtonListener;
	
	
	public static jumpListener JumpListener;
	
	public Main() {
		
		ImageIcon icon = new ImageIcon(this.getClass().getResource("assets/wolken.png"));
		width = icon.getIconWidth();
		height = icon.getIconHeight();
		
		background = icon.getImage();
		
		loadingScreen = new LoadingScreen(background, width, height);
		f.getContentPane().add(loadingScreen, SCREEN_WAIT);
		f.setVisible(true);
		
		start = new JButton("Spiel starten");
		start.addActionListener(this);
		add(start);
		
		Instance = this;
		
		LevelButtonListener LBListener = new LevelButtonListener();
		LButtonListener = LBListener;
		
		
		JumpListener = new jumpListener();
	}
	
	public static void main(String...args) {
		f = new JFrame("Jump and Run");
		f.getContentPane().setLayout(cardLayout);
		Main menu = new Main();
		
		f.setSize(width, height);
		f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		
		
		try {
			BufferedImage levelIcon = ImageIO.read(Instance.getClass().getResource("assets/levelIcon.png"));
			
			BufferedImage displayImage1 = DisplayImageCreator.create(ImageIO.read(Instance.getClass().getResource("assets/level/1.png")));
			BufferedImage displayImage2 = DisplayImageCreator.create(ImageIO.read(Instance.getClass().getResource("assets/level/2.png")));
			
			LEVEL_1 = new LevelIcon(ImageIO.read(Instance.getClass().getResource("assets/level/1.png")), /*ImageIO.read(Instance.getClass().getResource("assets/level/1_2.png"))*/displayImage1, ImageIO.read(Instance.getClass().getResource("assets/levelIcon.png")), "1");
			LEVEL_2 = new LevelIcon(ImageIO.read(Instance.getClass().getResource("assets/level/2.png")), displayImage2, levelIcon, "2");
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		buildLevelMenu(LEVEL_1, LEVEL_2);
		
		
		f.getContentPane().add(menu, SCREEN_MAIN_MENU);
		f.getContentPane().add(levelMenu, SCREEN_LEVEL_MENU);
		
		menu.setScreen(SCREEN_MAIN_MENU);
		currentScreenComponent = menu;
		
		System.out.println("visible");
		
	}
	
	
	private static void buildLevelMenu(LevelIcon...levels) {
		levelMenu.setLayout(new FlowLayout());
		for(int i = 0; i < levels.length; i++) {
			levelMenu.add(levels[i]);
			levels[i].addActionListener(LButtonListener);
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == start) {
			
			setScreen(SCREEN_LEVEL_MENU);
		}
		
	}
	
	@Override
	protected void paintComponent(Graphics g) {
		
		super.paintComponent(g);
		
		g.drawImage(background, 0, 0, this);
		
	}
	
	
	public void setScreen(String screen) {
		System.out.println("================================Current Screen: " + screen + "=======================");
		CurrentScreen = screen;
		cardLayout.show(f.getContentPane(), screen);
	}
	
	
		public void setGamePaused() {
				game.isPaused = true;
				game.pauseButton.setVisible(false);
				game.add(game.backToGame);
				game.add(game.restartLevel);
				game.add(game.backToLevelMenu);
				game.add(game.backToMainMenu);

				game.backToGame.setVisible(true);
				game.restartLevel.setVisible(true);
				game.backToLevelMenu.setVisible(true);
				game.backToMainMenu.setVisible(true);
				
				game.repaint();
				
		}
		
		public void setGameRunning() {
			game.isPaused = false;
			
			game.backToGame.setVisible(false);
			game.restartLevel.setVisible(false);
			game.backToLevelMenu.setVisible(false);
			game.backToMainMenu.setVisible(false);
			
			game.remove(game.backToGame);
			game.remove(game.restartLevel);
			game.remove(game.backToLevelMenu);
			game.remove(game.backToMainMenu);
			game.pauseButton.setVisible(true);
			System.out.println("running");
			
			synchronized(game) {
				game.notify();
				System.out.println("game: running");
			}
		}
		
		public void setGameRunningAfterDeath() {
			game.isDead = false;
			game.backToLevelMenu.setVisible(false);
			game.backToMainMenu.setVisible(false);
			game.remove(game.backToLevelMenu);
			game.remove(game.backToMainMenu);
			
			synchronized(game) {
				game.notify();
			}
		}
		
		public void restartLevel(BufferedImage displayImage, BufferedImage sourceImage) throws IOException {
			game.running = false;
			synchronized(game.game2) {
				game.game2.notify();
			}
			synchronized(game) {
				game.notify();
			}
			
			game = new Game(this, displayImage, sourceImage);
		}
	
	
	
	public class jumpListener implements KeyListener {

		@Override
		public void keyPressed(KeyEvent e) {
//			System.out.println("=============Pressed===============================");
			if(CurrentScreen.equals(SCREEN_RUNNING_GAME)) {
				
				if(e.getKeyCode() == KeyEvent.VK_SPACE) {
					/*if(!game.isFalling)
						game.isJumping = true;*/
					if(!game.isFalling)
						game.isJumping = true;
				}
				else if(e.getKeyCode() == KeyEvent.VK_ESCAPE && !game.isDead) {
					if(game.isPaused)
						setGameRunning();
					else
						setGamePaused();
				}
			}
		}

		@Override
		public void keyReleased(KeyEvent e) {
			if(CurrentScreen.equals(SCREEN_RUNNING_GAME)) {
				
				if(e.getKeyCode() == KeyEvent.VK_SPACE) {
					/*game.isJumping = false;
					game.isFalling = true;*/
					game.isJumping = false;
					game.isFalling = true;
				}
			}
		}

		@Override
		public void keyTyped(KeyEvent e) {
			
			
		}
		
	}
	
	public class LevelButtonListener implements ActionListener {
		
		private BufferedImage sourceImage;
		private BufferedImage displayImage;

//wenn ein Levelbutton gedrückt wird (startet Level)
		@Override
		public void actionPerformed(ActionEvent e) {
			if(loadingScreen == null)
				System.out.println("=============LoadingScreen = null!===============");
			
			if(e.getSource() instanceof LevelIcon) {
				Component[] c = f.getContentPane().getComponents();
				for(Component c2 : c) {
					System.out.println(c2.toString());
				}
				setScreen(SCREEN_WAIT);
				
				LevelIcon icon = (LevelIcon) e.getSource();
				
				sourceImage = icon.getSourceImage();
				displayImage = icon.getDisplayImage();
				
				try {
					game = new Game(Instance, sourceImage, displayImage);
					
					f.getContentPane().add(game, SCREEN_RUNNING_GAME);

					game.addKeyListener(new jumpListener());
					while(!game.finishedLoading) {
						
					}
					setScreen(SCREEN_RUNNING_GAME);

					game.setFocusable(true);
					game.requestFocus();
					game.start();
					
				} catch (IOException e1) {
					
					e1.printStackTrace();
				}
				
				
			}
	}
	
		
	}
	
	
}


Und die LoadingScreen-Klasse:

Java:
package ch.faberix.jumpandrun;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;

import javax.swing.*;

public class LoadingScreen extends JPanel {

	private Image background;
	private int width, height;
	
	public LoadingScreen(Image background, int width, int height) {
		super();
		this.background = background;
		this.setSize(width, height);
		this.width = width;
		this.height = height;
	}
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		//Hintergrund und Text zeichnen
		g.drawImage(background, 0, 0, null);
		g.setFont(new Font("sans", Font.PLAIN, 60));
		g.setColor(Color.DARK_GRAY);
		g.drawString("Ladevorgang", width/2 - 200, height/2 - 40);
	}
	
	
}

Weiss jemand, warum das nicht funktioniert und wie man es zum laufen bringt?
Wäre sehr froh um eine Lösung. 🙂
mfg, Faberix
 
Hi,

hm, das ist ein sehr merkwürdiger Aufbau den du da hast.
Main erweitert JPanel und implementiert ActionListener.
In main() erstellst du zuerst eine JFrame instanz, dann erstellst du eine neue Instanz von Main.
Im Main- Konstruktor setzt du dann f.setVisible() = true;
anschlissend gehts weiter in main() mit f.setSize(....) und f.setDefaultCloseOperation(...);

Das ist nicht so das gelbe vom Ei.
Ein paar Tips:
Erstelle eine Aussagekräfiten Klassen namen (z.B. GameGui)(Main ist nicht angebracht, da Verwechslungsgefahr mit main- Methode wahrscheinlich ist).
im GameGui Konstruktor erstellst du zuerst eine JFrame Instanz und setzt die Parameter; in etwa so:
Java:
public GameGui() {
    frame = new JFrame("My Gui");
    frame.setSize(710, 338);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
}
Dann erstelle eine Separate Klasse, welche die Applikation startet. z.B. GameTest.
In dieser Klasse ist eine initGui- Methode implementiert:
Java:
public void initGui() {
    ImageIcon icon = new ImageIcon(this.getClass().getResource("wolken.jpg"));
    int width = icon.getIconWidth();
    int height = icon.getIconHeight();
    Image background = icon.getImage();
    LoadingScreen loadingScreen = new LoadingScreen(background, width, height);
    GameGui gui = new GameGui();
    try {
        gui.getFrame().add(loadingScreen);
        Thread.sleep(5000);
        gui.getFrame().remove(loadingScreen);
        gui.initAllOther();
    } catch (InterruptedException e) {
        System.out.println(e.getStackTrace());
    }
}
Die Methode initGui() wird auf die GameTest- Instanz in main() aufgerufen:
Java:
public static void main(String[] args) {
    GameTest gt = new GameTest();
    gt.initGui();
}
Nun startet das Gui, zeigt den Loadingscreen für 5 sec und wechselt dann in die andere Ansicht.
hier mal die Klasse GameGui:
Java:
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class GameGui {
    private JFrame frame;
    private JPanel panel;
    private CardLayout cardLayout;
    private JButton exitButton;
    
    public GameGui() {
        frame = new JFrame("My Gui");
        frame.setSize(710, 338);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    public JFrame getFrame() {
        return frame;
    }
        
    public void initAllOther() {
        panel = new JPanel();
        panel.setLayout(cardLayout);    
        exitButton = new JButton("Exit");
        exitButton.setSize(100, 25);    
        exitButton.addActionListener(new MyActionListener());    
        panel.add(exitButton);
        frame.add(panel);
        frame.revalidate();    
    }
    
    class MyActionListener implements ActionListener {    
        @Override
        public void actionPerformed(ActionEvent e) {
            if(e.getSource().equals(exitButton)) {
                System.exit(0);
            }
            
        }
    }
}
und die TestKlasse GameTest:
Java:
import javax.swing.ImageIcon;
public class GameTest {
    public void initGui() {
        ImageIcon icon = new ImageIcon(this.getClass().getResource("wolken.jpg"));
        LoadingScreen loadingScreen = new LoadingScreen(icon.getImage(), icon.getIconWidth(), icon.getIconHeight());
        GameGui gui = new GameGui();
        try {
            gui.getFrame().add(loadingScreen);
            Thread.sleep(5000);
            gui.getFrame().remove(loadingScreen);
            gui.initAllOther();
        } catch (InterruptedException e) {
            System.out.println(e.getStackTrace());
        }
    }
    
    public static void main(String[] args) {
        GameTest gt = new GameTest();
        gt.initGui();
    }
}
Dies soll dir an einem Beispiel zeigen wie ein GUI aufgebaut werden kann. Es gibt verschiedene Arten wie das gemacht werden kann. Du kannst dies so implementieren wie du es möchtest, aber eine gewisse Reihenfolge musst du schon einhalten. Ansonsten wird nicht alles so funktionieren wie du es gerne möchtest 😉
 
Zuletzt bearbeitet:
Dein Problem liegt hier

Java:
setScreen(SCREEN_WAIT);
.........
while(!game.finishedLoading) {
 
}

Diese Schleife blockiert den UI Thread, so kann sich diese nicht wie gewünscht aktualisieren.
"Lang dauernde Auführungen" sollten immer in einen eigenen Thread ausgelagert werden damit der UI Thread nicht blockiert wird.

Anmerkung zum Code:
Die ganzen "static"s im Code sind sehr unsauber! Damit sind weitere Probleme vorprogrammiert.
Probiere "static" zu verhindern und mehr in die Richtung OOP zu gehen!
 
Hallo,

@knilch: Ich versuche, mein Projekt etwas übersichtlicher zu gestalten..., ich bin von Natur aus ein Chaot 🙂

@Joose: mit der weggelassenen while-Schleife funktioniert es immer noch nicht, ausserdem wird "game.finishedLoading" wenn der Konstruktor der Game-Klasse durchlaufen wurde auf true gesetzt.
 
@Joose: mit der weggelassenen while-Schleife funktioniert es immer noch nicht, ausserdem wird "game.finishedLoading" wenn der Konstruktor der Game-Klasse durchlaufen wurde auf true gesetzt.

Eine UI wird in Java im Event Dispatch Thread (EDT), da die Komponenten nicht Threadsafe sollte die UI nur vom EDT geändert werden.
Während der EDT aber beschäftigt ist kann er die UI nicht neuzeichnen.

Durch die Schleife sorgst du dafür das der EDT durchgehend beschäftigt ist und keine Zeit hat die UI neu zu zeichnen (dein WAIT Screen wird nicht gezeigt).
Lagere das Laden von einem Level in einen eigenen Thread aus.
 
Jetzt funktioniert's!

Danke Joose.

Mein Code sieht jetzt so aus:

Java:
public class LevelButtonListener implements ActionListener, Runnable {
		
		private BufferedImage sourceImage;
		private BufferedImage displayImage;
		
		private Object button;

		@Override
		public void actionPerformed(ActionEvent e) {
			if(loadingScreen == null)
				System.out.println("=============LoadingScreen = null!===============");
			
			setScreen(SCREEN_WAIT);
			button = e.getSource();
			Thread thread = new Thread(this);
			thread.start();
	}

		@Override
		public void run() {
			if(button instanceof LevelIcon) {
				Component[] c = f.getContentPane().getComponents();
				for(Component c2 : c) {
					System.out.println(c2.toString());
				}
				
				LevelIcon icon = (LevelIcon) button;
				
				sourceImage = icon.getSourceImage();
				displayImage = icon.getDisplayImage();
				
				try {
					game = new Game(Instance, sourceImage, displayImage);
					
					f.getContentPane().add(game, SCREEN_RUNNING_GAME);

					game.addKeyListener(new jumpListener());
					while(!game.finishedLoading) {
						
					}
					setScreen(SCREEN_RUNNING_GAME);

					game.setFocusable(true);
					game.requestFocus();
					game.start();
					
				} catch (IOException e1) {
					
					e1.printStackTrace();
				}
				
				
			}
			
		}
	
		
	}
 

Zurück
Oben