Swing Background Image in JFrame implementieren

Diskutiere Background Image in JFrame implementieren im AWT, Swing, JavaFX & SWT Bereich.
R

Ruffyg

Guten Morgen,
ich habe vor kurzem angefangen mich für Java zu interessieren. Nach einigen Tagen habe ich beschlossen das Spiel Flappy Bird nach zu programmieren.
Ich habe versucht es Objektorientiert zu gestalten. Ich nutze Apache NetBeans IDE 11.2 als Entwicklungsumgebung. Es funktioniert bisher ganz gut, einige Features wie ein Score der nach oben zählt pro geschaffte Röhre fehlt noch (Score geht derzeit nur um 1 hoch per Klick). Aber das ist gar nicht das Problem.

Mein Problem ist das ich eine BackgroundPanel.java Class habe und gerne in meiner initComponents Methode mein Hintergrundbild aufrufen möchte welches im src Bereich liegt.

Sobald ich jedoch versuche per initComponents mein Hintergrundbild anzeigen zu lassen, bekomme ich eine Compiler Fehlermeldung.

Code:
warning: [options] bootstrap class path not set in conjunction with -source 8
1 warning
compile:
run:
Exception in thread "main" java.lang.IllegalArgumentException: input == null!
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1400)
    at flappybird.gui.BackgroundPanel.<init>(BackgroundPanel.java:18)
    at flappybird.gui.Hauptfenster.initComponents(Hauptfenster.java:62)
    at flappybird.gui.Hauptfenster.<init>(Hauptfenster.java:45)
    at flappybird.FlappyBird.main(FlappyBird.java:21)
D:\Fritz\Desktop\FlappyBird\nbproject\build-impl.xml:1330: The following error occurred while executing this line:
D:\Fritz\Desktop\FlappyBird\nbproject\build-impl.xml:968: Java returned: 1
BUILD FAILED (total time: 3 seconds)
Es wäre sehr nett, wenn jemand mein Programm etwas ausbessern könnte. Ich habe das Aufrufen des Hintergrundbildes in meiner initComponent Methode auskommentiert damit das Programm zumindest läuft :)

Hier könnt ihr euch den Ordner herunterladen:

Mein Code ist bestimmt nicht sauber und fehlerfrei, also macht euch schon mal darauf gefasst nach hinten umzukippen

Mit freundlichen Grüßen Fritz
 
J

JustNobody

Also ein erster Ansatz wäre in meinen Augen:
img = ImageIO.read(getClass().getResource("/hintergrund.jpg"));

Das Laden der Ressourcen erfolgt relativ zu der Klasse. "hintergrund.jpg" würde also am Ort der .class Datei suchen. Das Bild ist aber im root der Klassen zu finden, daher "/hintergrund.jpg".

Das wäre jetzt zumindest auf den ersten Blick das, was ich meine zu sehen.

Siehe z.B. https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResource(java.lang.String) für die genaue Erklärung.

Edit: Ursache ist natürlich, dass getResource die Ressource nicht finden kann und daher null zurück gibt. Damit kann dann ImageIO.read auch kein Bild laden.
 
R

Ruffyg

Also ein erster Ansatz wäre in meinen Augen:
img = ImageIO.read(getClass().getResource("/hintergrund.jpg"));

Das Laden der Ressourcen erfolgt relativ zu der Klasse. "hintergrund.jpg" würde also am Ort der .class Datei suchen. Das Bild ist aber im root der Klassen zu finden, daher "/hintergrund.jpg".

Das wäre jetzt zumindest auf den ersten Blick das, was ich meine zu sehen.

Siehe z.B. https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResource(java.lang.String) für die genaue Erklärung.

Edit: Ursache ist natürlich, dass getResource die Ressource nicht finden kann und daher null zurück gibt. Damit kann dann ImageIO.read auch kein Bild laden.
Ich habe es ausprobiert, ich bekomme keine Fehlermeldung mehr, jedoch ist auch kein Hintergrund zu sehen. Er findet denke ich die Ressource aber vermutlich fehlt ihm eine Eigenschaft wie zum Beispiel visibility.
 
J

JustNobody

Also mir fällt da nicht wirklich etwas auf. Außer das ich beim drawImage statt this als letzten Parameter ein null gesetzt hätte. Aber das sollte eigentlich nichts ausmachen....

Das Bild ist richtig, hat auch eine Größe, so dass dies gesehen werden sollte und so?
 
R

Ruffyg

Also mir fällt da nicht wirklich etwas auf. Außer das ich beim drawImage statt this als letzten Parameter ein null gesetzt hätte. Aber das sollte eigentlich nichts ausmachen....

Das Bild ist richtig, hat auch eine Größe, so dass dies gesehen werden sollte und so?
1271912720

NetBeans meckert ja auch nicht, deshalb weiß ich echt nicht wo das Problem ist :/
 
R

Ruffyg

Die Try and Catch Funktion gibt auf der Konsole "Picture loaded" aus, was ja eigentlich nur passieren soll wenn es geklappt hat
 
R

Ruffyg

12721
Habe jetzt wie du sagtest paint mit paintComponent ausgetauscht. Das Problem ist das Bild hat anscheinend die höchste Priorität und ist im Vordergrund. Aber ich möchte ja das es die unterste Ebene ist
 
mihe7

mihe7

Ja, die Komponenten bilden eine Hierarchie. Unten liegt die "content pane" Deines JFrames. Darüber liegen die Komponenten, die Du der content pane hinzugefügt hast usw. Gezeichnet wird von unten nach oben.

Das Spielfeld muss ja über dem Hintergrund liegen, d. h. entweder zeichnest Du beides in einer Komponente (erst Hintergrund, dann Spielfeld) oder Du fügst eine Spielfeldkomponente der Hintergrundkomponente hinzu.
 
R

Ruffyg

Ja, die Komponenten bilden eine Hierarchie. Unten liegt die "content pane" Deines JFrames. Darüber liegen die Komponenten, die Du der content pane hinzugefügt hast usw. Gezeichnet wird von unten nach oben.

Das Spielfeld muss ja über dem Hintergrund liegen, d. h. entweder zeichnest Du beides in einer Komponente (erst Hintergrund, dann Spielfeld) oder Du fügst eine Spielfeldkomponente der Hintergrundkomponente hinzu.
12726

Habe jetzt eine initHintergrund Methode geschrieben und Sie mit this.initHintergrund() als aller erstes aufgerufen. Trotzdem ist mein Hintergrund noch die oberste Ebene und steht über allem :/

Ja, die Komponenten bilden eine Hierarchie. Unten liegt die "content pane" Deines JFrames. Darüber liegen die Komponenten, die Du der content pane hinzugefügt hast usw. Gezeichnet wird von unten nach oben.

Das Spielfeld muss ja über dem Hintergrund liegen, d. h. entweder zeichnest Du beides in einer Komponente (erst Hintergrund, dann Spielfeld) oder Du fügst eine Spielfeldkomponente der Hintergrundkomponente hinzu.
Ich weiß leider wirklich nicht mehr weiter. Habe locker 1h versucht innerhalb einer Initialisierung Hintergrund und Spielfeld aufzurufen, aber entweder hat es gar nicht funktioniert oder mein Hintergrund war der Vordergrund :/
 
R

Ruffyg

Java:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package flappybird.gui;

import flappybird.model.Bar;
import flappybird.model.Bird;
import flappybird.model.Punkte;
import flappybird.model.Boden;
import flappybird.model.Spielfeld;
import static java.awt.Color.black;
import static java.awt.Color.green;
import java.awt.Graphics;
import java.awt.Graphics2D;
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.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
*
* @author Fritz
*/
public class Hauptfenster extends JFrame implements ActionListener,MouseListener,KeyListener{
   
    private Spielfeld spielfeld = new Spielfeld();
    private Punkte punkte = new Punkte();
   
    private int interval = 25;
    private Timer timer = new Timer(interval, this);
   
    public Hauptfenster(){
       
        super("FlappyBird");
       
        this.initHintergrund();
        this.initSpielfeld();
        this.initComponents();
       
       
    }
   
    private void initHintergrund(){
        BackgroundPanel backgroundPanel = new BackgroundPanel();
    add(backgroundPanel);

    setSize(backgroundPanel.getBackgroundImage().getWidth(backgroundPanel),    backgroundPanel.getBackgroundImage().getHeight(backgroundPanel));
    }
   
   
    private void initSpielfeld(){
   
        this.spielfeld.setBreite(800);
        this.spielfeld.setHoehe(600);
        Bird bird = this.spielfeld.getBird();
        bird.setxPos(100);
        bird.setyPos(100);
        bird.setBreite(100);
        bird.setHoehe(100);
       
       
    }
   
    private void initComponents(){
        this.setSize(800, 600);
        this.addMouseListener(this);
        this.addKeyListener(this);
       
        this.timer.start();
       
    }

      public void paintComponent(Graphics g) {
        Graphics2D graphics = (Graphics2D) g;

        Bird bird = this.spielfeld.getBird();
       
        graphics.clearRect(0,0,800, 600);
       
        //Vogel zeichnen
        graphics.drawRect(bird.getxPos(), bird.getyPos(), bird.getBreite(), bird.getHoehe());
       
        //Boden zeichnen
        for(Boden boden : this.spielfeld.getBoden()){
            graphics.setColor(green);
            graphics.fillRect(boden.getxPos(), boden.getyPos(), boden.getBreite(), boden.getHoehe());
           
        }
       
        //BottomBars zeichnen
        for(Bar b : this.spielfeld.getBottomBars()){
            graphics.setColor(black);
            graphics.fillRect(b.getxPos(), b.getyPos(), b.getBreite(), b.getHoehe());
        }
       
        //TopBars zeichnen
        for(Bar b : this.spielfeld.getTopBars()){
            graphics.setColor(black);
            graphics.fillRect(b.getxPos(), b.getyPos(), b.getBreite(), b.getHoehe());
        }
       
       
    }
   
   
   
   
   

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("ActionPerformed " + e.getActionCommand());
       
        this.spielfeld.update(this.interval);
       
        this.repaint();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("MouseClicked");
        Bird bird = this.spielfeld.getBird();
        bird.setyPos(bird.getyPos()-50);
        punkte.plus();
        System.out.println("Ihr Punktestand hat sich um eins erhöht. Er beträgt nun: " + punkte.getPunktestand());
       
    }

    @Override
    public void mousePressed(MouseEvent e) {
       
    }

    @Override
    public void mouseReleased(MouseEvent e) {
       
    }

    @Override
    public void mouseEntered(MouseEvent e) {
       
    }

    @Override
    public void mouseExited(MouseEvent e) {
       
    }

    @Override
    public void keyTyped(KeyEvent e) {
       
    }

    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println("KeyPressed " + e.getKeyCode());
    }

    @Override
    public void keyReleased(KeyEvent e) {
       
    }
   
   
}
Meine Spielfeld Klasse
 
mihe7

mihe7

Gezeichnet wird nicht irgendwo (schon gar nicht in einer Initialisierung), sondern in paintComponent - die Methode hat den Namen nicht zum Spaß :)

Und: ich meinte nicht, dass Du paintComponent des hinzugefügten Hintergrundbildes aufrufen sollst, das bringt gar nichts.

Du hast drei Alternativen:
1. Du sorgst dafür, dass drawImage in paintComponent Deines Hauptfensters aufgerufen wird. Die Komponente Hintergrundbild darf dann nicht verwendet werden.
2. Du sorgst dafür, dass der Zeichencode aus Deinem Hauptfenster und drawImage in einer neuen Komponente (neue Klasse) aufgerufen werden. Die Komponente Hintergrundbild darf dann nicht verwendet werden.
3. Du verschiebst nur den Zeichencode aus Deinem Hauptfenster in eine neue Komponente (neue Klasse) und baust dann die entsprechende Komponentenhierachie auf: Hauptfenster enthält Hintergrundbild enthält Spielfeld
 
R

Ruffyg

Gezeichnet wird nicht irgendwo (schon gar nicht in einer Initialisierung), sondern in paintComponent - die Methode hat den Namen nicht zum Spaß :)

Und: ich meinte nicht, dass Du paintComponent des hinzugefügten Hintergrundbildes aufrufen sollst, das bringt gar nichts.

Du hast drei Alternativen:
1. Du sorgst dafür, dass drawImage in paintComponent Deines Hauptfensters aufgerufen wird. Die Komponente Hintergrundbild darf dann nicht verwendet werden.
2. Du sorgst dafür, dass der Zeichencode aus Deinem Hauptfenster und drawImage in einer neuen Komponente (neue Klasse) aufgerufen werden. Die Komponente Hintergrundbild darf dann nicht verwendet werden.
3. Du verschiebst nur den Zeichencode aus Deinem Hauptfenster in eine neue Komponente (neue Klasse) und baust dann die entsprechende Komponentenhierachie auf: Hauptfenster enthält Hintergrundbild enthält Spielfeld
Habe mich für Alternative Nummer Eins entschieden, da ich faul bin und keine Lust habe eine vollständige Hierarchie zu programmieren (wüsste auch nicht wie es geht).

Mit g.drawImage() innerhalb von paintComponents soll ich arbeiten... aber ich weiß nicht wie die Methode funktioniert. Ich verstehe absolut nicht die Dokumentation von drawImage

Java:
g.drawImage("/hintergund.jpg", 800, 600, contentPane);

Bei rootPane habe ich contentpane auf gut Glück probiert, funktioniert leider nicht.

Tut mir leid das ich so blöd nachfragen muss, es ist schließlich nicht selbstverständlich das du mir hilfst... aber das ich an so einem kleinen Problem so lange sitzen muss macht mich echt fertig haha.
 
mihe7

mihe7

Hä? Du hast doch drawImage schon in BackgroundPanel verwendet, dann musst Du doch wissen, wie es geht.
 
R

Ruffyg

Hä? Du hast doch drawImage schon in BackgroundPanel verwendet, dann musst Du doch wissen, wie es geht.
Nein, ich weiß eben nicht wie es funktioniert. Das hat mir NetBeans nach 100 von Fehlermeldungen zusammen gebaut haha.

Java:
        Container contentPane = rootPane.getContentPane() ;
        g.drawImage("hintergrund.jpg", 800, 600, contentPane);
Das sieht schon etwas besser aus, aber er findet einfach das hintergrund.jpg nicht
 
J

JustNobody

Also Du hast Code in einer Klasse der funktioniert. Unabhängig davon, ob du da irgendwie mit trial and error etwas erstellt hast oder ob es vom Himmel gefallen ist: Du solltest Code immer verstehen! Deine Situation derzeit ist also:
- Du hast Code, den du nicht verstehst.
- Du hast keine Tests (mit vernünftiger Abdeckung)
- Du willst da irgend etwas bauen, dass doch ein kleines bisschen größer ist als ein "Hello world" oder ein kleines Beispiel aus dem Unterricht.

==> Das wird in großem Frust enden. Wenn Du das willst: Da gibt es bestimmt einfachere Wege.

Aus meiner Sicht ist es existenziell, dass Du den Code, den Du verwendest, 100% verstehst! Wenn irgendwann etwas nicht mehr geht, hast Du sonst 0 Chance, den Fehler zu beheben und das wird massiven Frust mit sich bringen!

Der zweite Punkt ist da ansonsten auch sehr wichtig, aber da gehe ich jetzt nicht weiter drauf ein. Da ist der Hintergrund, dass Du Änderungen am Code direkt vollständig prüfen kannst. Du vermeidest also die Situation, dass Du nach und nach immer mehr kaputt machst und dann, wenn es auffällt, hast Du kaum eine Chance, Fehler zielgerichtet zu finden und zu beheben, da Du einfach eine größere Menge an Fehlern hast, die sich ja auch gegenseitig beeinflussen.

Was hast Du denn in deinem BackgroundPanel?
a) img = ImageIO.read(getClass().getResource("hintergrund.jpg"));
b) g.drawImage(img, 0, 0, this);

Was machen diese beiden Zeilen Code? Zu erstem habe ich dir unter anderem auch einen Link geschickt (Das hat schon den Sinn, dass man sich den etwas ansieht :) )...
 
R

Ruffyg

a) img = ImageIO.read(getClass().getResource("hintergrund.jpg"));
b) g.drawImage(img, 0, 0, this);
a) benutzt die die ImageIO Klasse mit der .getResource() Methode um mein Bild zu finden und initialisiert damit die img Variable.
b) bei g.drawImage(img, 0, 0, this) müsste mein Bild geladen werden, aber mit den Abmaßen von 0px Breite und 0px Höhe und this ist eine Referenz auf ??? ... ja da bin ich überfragt....

Ich weiß wie ich auf meinen Hintergrund zugreife aber nicht wie ich ihn zeichne... und zwar im Hintergrund, weil ich die Methoden von paintContent nicht kenne. Das ist wirklich frustrierend, aber ich denke nicht wirklich eine Liga zu hoch für mich...
 
J

JustNobody

ImagieIO.read liest ein Bild aus einer Ressource und erstellt ein Image Objekt. Damit bekommst Du also ein Abbild des Bildes im Speicher.
Bei der paintComponent Methode wird eine Graphics übergeben. Diese bietet Methoden zum Malen und Zeichnen. Eine Methode ist das drawImage, welches ein Bild malt. Und als Parameter bekommt es das Bild, das gemalt werden soll und die x/y Koordinate wo es hin gemalt werden soll. Das letzte ist ein ImageOberserver - Wie der Name schon sagt, kommt hier ein ObserverPattern zum tragen. Denn wichtig ist in dem Zusammenhang auch der Return Code. Wenn false zurück gegeben wurde, dann verändert sich das Bild noch. In so einem Fall wird der ImageObserver benachrichtigt, wenn das Bild fertig gemalt wurde.
(Braucht man hier nicht, da kann also auch null statt this stehen).

https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver)

Ich würde Dir raten, die mit der Dokumentation des Java Frameworks zu beschäftigen. Diese Dokumentation ist immer zu rate zu ziehen, wenn man da etwas machen will.... Auch wenn es am Anfang noch unübersichtlich scheint: Schau einmal drüber und probiere dann von mir aus an Beispielen rum. Und wenn etwas läuft, dann schau noch einmal in der Dokumentation nach. So kommt dann nach und nach ein Verständnis der Dokumentation auf!

Edit: Ach ja: http://openbook.rheinwerk-verlag.de/javainsel9/javainsel_20_006.htm
 
R

Ruffyg

ImagieIO.read liest ein Bild aus einer Ressource und erstellt ein Image Objekt. Damit bekommst Du also ein Abbild des Bildes im Speicher.
Bei der paintComponent Methode wird eine Graphics übergeben. Diese bietet Methoden zum Malen und Zeichnen. Eine Methode ist das drawImage, welches ein Bild malt. Und als Parameter bekommt es das Bild, das gemalt werden soll und die x/y Koordinate wo es hin gemalt werden soll. Das letzte ist ein ImageOberserver - Wie der Name schon sagt, kommt hier ein ObserverPattern zum tragen. Denn wichtig ist in dem Zusammenhang auch der Return Code. Wenn false zurück gegeben wurde, dann verändert sich das Bild noch. In so einem Fall wird der ImageObserver benachrichtigt, wenn das Bild fertig gemalt wurde.
(Braucht man hier nicht, da kann also auch null statt this stehen).

https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver)

Ich würde Dir raten, die mit der Dokumentation des Java Frameworks zu beschäftigen. Diese Dokumentation ist immer zu rate zu ziehen, wenn man da etwas machen will.... Auch wenn es am Anfang noch unübersichtlich scheint: Schau einmal drüber und probiere dann von mir aus an Beispielen rum. Und wenn etwas läuft, dann schau noch einmal in der Dokumentation nach. So kommt dann nach und nach ein Verständnis der Dokumentation auf!

Edit: Ach ja: http://openbook.rheinwerk-verlag.de/javainsel9/javainsel_20_006.htm
Danke das ist echt super nützlich , aber ich könnte mittlerweile echt weinen. Habe versucht in meiner paintComponent KLASSE ein neues Objekt der KLASSE paintComponent mit dem Namen img zu erzeugen. Wie erwartet hat es nicht geklappt. Auf diesem Objekt img wollte ich gerne mit IO.Read das Bild als Parameter übergeben.

img = ImageIO.read(getClass().getResource("/hintergrund.jpg"));

Alles ab getClass war rot unterstrichen...

Somit war es spätestens klar das drawImage auch nicht funktioniert...

Mein Code sieht jetzt so aus aber natürlich geht es nicht

Java:
public void paintComponent(Graphics g) {
        Graphics2D graphics = (Graphics2D) g;
        paintComponent img = new paintComponent();
        img = ImageIO.read(getClass().getResource("/hintergrund.jpg"));
        g.drawImage(img, 0, 0, null);
        
        Bird bird = this.spielfeld.getBird();
        
        graphics.clearRect(0,0,800, 600);
        
        //Vogel zeichnen
        graphics.drawRect(bird.getxPos(), bird.getyPos(), bird.getBreite(), bird.getHoehe());
        
        //Boden zeichnen
        for(Boden boden : this.spielfeld.getBoden()){
            graphics.setColor(green);
            graphics.fillRect(boden.getxPos(), boden.getyPos(), boden.getBreite(), boden.getHoehe());
            
        }
Warum kann ich kein Objekt img mit dem Standartkonstruktur von paintComponent erstellen?
Warum findet er mein hintergrund.jpg nicht?
 
Thema: 

Background Image in JFrame implementieren

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben