JPanel zeichnet im Konstrukter erzeugten Hintergrund nicht

Luschi

Mitglied
Hallo Leute,
ich bin gerade dabei ein kleines Spiel zu programmieren. Ich habe eine Klasse "Boot", welche durch die Mainmethode aufgerufen wird. Diese erzeugt ein Frame, dem dann ein Gamepanel hinzugefügt wird. Dieses Gamepanel ist ebenfalls eine eigene Klasse und in ihrem Konstruktor möchte ich einer Klassenvariable vom Typ BufferedImage ein .png Bild aus dem Package res zuweisen. Dieses wird in der paintComponent-Methode gezeichnet. Als Test ob überhaupt etwas gezeichnet wird habe ich einfach irgendein Sys.out in paintComponent verwendet. Leider läuft die paintComponent-Methode überhaupt nicht, wenn ich versuche ein Bild im Konstruktor zu laden, selbst wenn g.drawImage(background1 ...) auskommentiert ist. Wenn ich das Fenster mit der Maus etwas nach links ziehe, fängt es plötzlich an zu zeichnen. Kann mir wer erklären wieso? Meine Vermutung wäre ja, dass der Konstrukter "zu lange dauert", da ich bereits Thread.sleep verwendet habe und aufs gleiche Ergebnis gekommen bin.

Edit: Ich sollte noch erwähnen, dass der Hintergrund nie gezeichnet wird, auch wenn ich das Fenster verschiebe und somit das Rechteck erscheint.

Code:
package res;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class GamePanel extends JPanel{
  
    protected Player player1;
    protected BufferedImage background;
  
    public GamePanel(Player player1){

        this.player1 = player1;
        System.out.println(Boot.zahl);
        createBackground();
    }
  
    protected void createBackground(){
          try {
                background = ImageIO.read(getClass().getClassLoader().getResourceAsStream("res/background1.png"));
           } catch (IOException e) {
              
                e.printStackTrace();
           }
    }
  
    protected void paintComponent(Graphics g){
      
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      
        System.out.println("fenfr");
        //g.drawImage(background, 0, 0, null);

        g.drawRect(50, 50, 40, 50);
        //g.drawImage(player1.getImagePlayer(), player1.getPosX(), player1.getPosY(), null);
        repaint();
      
    }
  
}

MfG
Luuschi
 
Moin,
also so wie ich das jetzt verstanden habe erzeugst du in dieser Klasse das Panel worauf der Hintergrund zu sehen sein soll richtig?
Mit der Methode createPanel() lädst du meine erachtens zwar das Bild, aber wo teilst du dem Panel dieses hinzu? Da müsste doch irgendwo ein "add" oder so stehen, womit dem Panel das geladene Bild dann auch zuweist.
Das wäre jetzt so meine erste Vermutung ..

MfG Soloeco
 
Hi erstmal danke für die Antwort. Ich habe das Panel dem Frame geaddet. Ich möchte dem Panel nicht explizit sagen, dass es der Background sein soll. Eher möchte ich einfach ein Bild über das gesamte Panel zeichen ( //g.drawImage(background, 0, 0, null)😉. In diesem Fall habe ich es erstmal ausgeklammert um zu sehen wo eigentlich das Problem liegt. Denn wenn ich es ganricht zeichne und nur im Konstrukor einer Variable einem Bild zuweise müsste es eigentlich das Rechteck zeichnen. Tut es aber nicht bzw. die paintComponent methode wird nicht richtig aufgerufen, denn ( System.out.println("fenfr")😉 wird nicht in der Konsole ausgegeben. Wenn ich das Background Bild in einer eigenen Klasse initialisiere und der drawImage methode als g.drawImage(background1.getImageBackground(), 0, 0, null) übergebe sollte es funktionieren. Siehe (//g.drawImage(player1.getImagePlayer(), player1.getPosX(), player1.getPosY(), null);.

Edit: Mich würde halt nur interessieren wieso es ein Problem ist, ein Bild in dem Konstruktor der Panel Klasse zuzuweisen.
 
2 Dinge:

1.Du solltest auf deiner Methode das @Override Tag hinzufügen um auch wirklich die Methode aus der Vaterklasse zu überschreiben.
2.Da du meintest dass du ein Spiel machst, solltest du super.paintComponent(g) aufrufen um sicher zu gehen, dass du immer neu zeichnest 🙂

Gruß
Robert
 
Wow.. Ich habe beim auslagern des Panels die Superklasse vergessen... Vielen Dank ich werde mal probieren obs damit funtkioniert. Zum @Override , ich dachte immer das ist nur eine Art Kommentar für den Benutzer, gut dass du das sagst. Hab erst vor kurzem richtig mit Java angefangen =)
 
Okay ich habe die Initialisierung des Hintergrundes in eine Klasse Background ausgelagert. Dieser Code funktioniert und zeichnet alles:

Code:
package res;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class GamePanel extends JPanel{
   
    protected Player player1;
    protected Background background1;
    protected BufferedImage background2;
   
    public GamePanel(Player player1, Background background1){

        this.player1 = player1;
        this.background1 = background1;
       
       
    }
   
    @Override
    protected void paintComponent(Graphics g){
       
        super.paintComponent(g);
       
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
       
        System.out.println("fenfr");
       
        g.drawImage(background1.getImageBackground(), 0, 0, null);
        g.drawRect(50, 50, 40, 50);
        g.drawImage(player1.getImagePlayer(), player1.getPosX(), player1.getPosY(), null);
        repaint();
       
    }
   
}

Wenn ich jetzt allerdings auf mein Verständnisproblem von vorhin zurück komme und im Konstruktor noch ein Image zuordnen möchte, bleibt das Fenster wieder weiß:

Code:
public GamePanel(Player player1, Background background1){

        this.player1 = player1;
        this.background1 = background1;
        try {
            background2 = ImageIO.read(getClass().getClassLoader().getResourceAsStream("res/background1.png"));
           } catch (IOException e) {
           
            e.printStackTrace();
           }
       
    }

Wenn ich jetzt die Image Zuweisung noch durch Thread.sleep() ersetze merke ich, dass es reibungslos funktioniert bis Thread.sleep() >= 9, also wenn der Konstruktor länger als 9 Millisekunden dauert wird nichts mehr gezeichnet. Es handelt sich hierbei nur um ein reines Verständnisproblem meinerseits. Ich würde einfach gerne Wissen wieso das passiert.
 
Das riecht verdächtig nach einer race conditon.
Vermutlich läuft das einlesen des Bildes asynchron, d. h. es ist möglich dass das Bild noch nicht vollständig oder ev. gar nicht geladen wurde, bevor es auf dem Graphics2D gezeichnet werden soll. Und bei einem null oder unvollständigen Image macht Graphics.drawImage stillschweigend nichts.
Falls das Bild noch nicht geladen wurde, müsste aber eigentlich ein Thread.sleep() am Ende des Konstruktors von GamePanel helfen, weil dabei der GamePanel-Konstruktionsthread pausieren müsste und nicht der Bildlade-Thread, und letzteres somit mehr Zeit haben müsste, seine Arbeit zu erledigen. Die Formulierung aus deinem letzten Beitrag klingt aber so, als wäre es genau andersrum, wobei mir nicht ganz klar ist, was du genau wo wodurch ersetzt.
Das repaint() am Ende der paintComponent() sieht aber in jedem Fall falsch aus. repaint() sollte erst dann aufgerufen werden, wenn sich der Zustand einer Komponente geändert hat (andere Größe, andere Farbe, etc.), und definitiv nicht aus der paintComponent, da sich ansonsten eine Endlosschleife ergibt.
 
Das repaint() kommt natürlich in die Run Methode. Ich habe hier nochmal ne neue Klasse geschrieben um meine Frage deutlicher zu stellen :

Code:
public class Panel extends JPanel{

   
    public Panel(){
         try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        
         repaint();
    }
   
    public void paint(){
        repaint();
    }
    @Override
    protected void paintComponent(Graphics g){
       
        super.paintComponent(g);
       
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
       
        g.drawRect(50, 50, 50, 50);
       
    }
}

Es wird nichts gezeichnet. Wenn Thread.sleep(10) ist wird etwas gezeichnet. Kann es sein das paintComponent() irgendwie auf den Konstruktor wartet und wenns zu lange dauert nichts macht ? Aber dann müsste repaint() im Konstruktor doch die Methode nochmal aufrufen?
 
Ist es nicht so, dass Thread.sleep(n); den EDT (Event Dispatch Thread) blockiert und die GUI damit "einfriert" -> nichts mehr passiert?
Ein Timer/TimerTask wäre da sicherlich angemessener um das blockieren zu verhindern.
 

Zurück
Oben