# Bild wird nicht angezeigt



## Shiwayari (5. Aug 2009)

Ich versuche nun schon seit ein paar Stunden hinzubekommen ein Bild in einem JPanel anzuzeigen.. habe zwar schon mehrere Threads hier dazu gefunden, das Problem aber immer noch nicht lösen können, also hier nochmal spezieller.

Erstmal der relevante Code:


```
...
	private Image cover;
	private JPanel pImage = new JPanel(null);
...	
	public void loadCover() {
		cover = Toolkit.getDefaultToolkit().createImage("***\\"+name+".jpg");  //'***' ist nur Zensur, so natuerlich nicht im Code..
		MediaTracker mt = new MediaTracker(pImage);
		mt.addImage(cover, 0);
		try{
			mt.waitForAll();
			System.out.println("1");
		}catch(InterruptedException e){System.out.println("2");}
		System.out.println("3");
		if(!pImage.getGraphics().drawImage(cover, 0, 0, 150, 200, pImage)){
			System.out.println("4");;
		}
	}
...
```

Ausgegeben wird 1 und 3, das laden sollte also doch funktioniert haben? Warum 2 nicht ausgegeben wird ist mir jedoch ein Rätsel..

Das JPanel pImage ist ausserdem in einem weiteren Panel mit GridBagLayout, welches wiederrum in einem weiterem JPanel in einem JScrollPane ist.. Alles bis auf das erste JPanel im JScrollPane wird zur Laufzeit erstellt und angezeigt, nur das Bild will er nicht anzeigen.

Habe auch schon mit repaint und validate rumprobiert, hat sich aber nichts getan. :noe:

Kann wer helfen?


----------



## André Uhres (5. Aug 2009)

Versuch's mal so:

```
private JLabel pImage = new JLabel();
public void loadCover(String name) {
    //wenn das Bild im Verzeichnis der Klasse steht,
    //genügt diese relative Pfadangabe:
    pImage.setIcon(new ImageIcon(getClass().getResource(name + ".jpg")));
}
```
Siehe auch: Background Panel  Java Tips Weblog


----------



## Shiwayari (5. Aug 2009)

Also als JLabel und Icon geht das ganze. Ich bräuchte aber etwas indem sich das Bild noch skalieren lässt. Habe mal über das BackgroundPanel geschaut und ausprobiert, aber mal abgesehen davon, dass ich nicht 100%ig weiß wie das zu benutzen ist und es auch noch nicht funktioniert hat, sollte es auch ohne externe Klasse möglich sein ein simples Bild skaliert anzuzeigen..

Habe nochmal eine kurze test-Klasse geschrieben, bei der einfach nur das Bild in einem Panel angezeigt werden soll:


```
import java.awt.*;
import javax.swing.*;

public class Test extends JFrame {
	
	Image img;
	JPanel panel = new JPanel(null);

	public static void main(String[] args) {
		Test t = new Test();
	}	

	public Test() {
		super("test");
		Container cp = this.getContentPane();
		cp.setLayout(new FlowLayout());
		setVisible(true);
		panel.setBounds(20, 20, 200, 200);
		cp.add(panel);
		img = Toolkit.getDefaultToolkit().createImage(getClass().getResource("test.jpg"));
		MediaTracker mt = new MediaTracker(panel);
		mt.addImage(img, 0);
		try{
			mt.waitForAll();
		}catch(InterruptedException e){}
		panel.getGraphics().drawImage(img, 0, 0, panel);
	}	
	
	public void paint(Graphics g){
		g.drawImage(img, 0, 0, panel);
	}
}
```

Wenn ich das nun so mache, dann wird das Bild angezeigt, und zwar im Frame, nicht im Panel. ???:L

Heißt also, bei Aufruf von drawImage wird immer die paint() Methode aufgerufen (wenn ich die paint() Methode lösche, wird nichts angezeigt, auch wenn sie nicht direkt aufgerufen wird); und scheinbar immer mit dem Graphics Objekt des Frames, obwohl ich das des JPanels übergeben habe.. (wenn ich cp.add(panel) lösche, bekomme ich zwar eine NullPointerException, das Bild ist aber trotzdem noch da..)

Da die Klasse in meinem ersten Post jedoch nicht von JFrame abgeleitet ist, wird auch nichts angezeigt. Darin wird nur eine Instanz eines Objektes erstellt, das als "Visualisierung" ein JPanel benutzt, welches dann in einer anderen von JFrame abgeleiteten Klasse angezeigt wird..

Aber es muss doch irgendwie möglich sein ein Bild in einem Panel und nicht im Frame anzuzeigen?!


----------



## Marco13 (6. Aug 2009)

Hui, da geht's ja hübsch durcheinander. 

Das 
panel.getGraphics().drawImage(img, 0, 0, panel);
hat mit der paint-Methode darunter nichts zu tun.

Die paint-Methode ist die paint-Methode des _JFrames_. In dieser Methode steht drin, was gemacht werden muss, wenn der _JFrame_ gezeichnet wird. Im Moment hast du die überschrieben, und sagst: "Wenn der JFrame gezeichnet werden soll, soll NUR das Bild gezeichnet werden - und sonst nichts". Im allgemeinen sollte man die paint-Methode eines JFrames NICHT überschreiben. Das Zeichnen sollte in einem JPanel stattfinden. Aber auch nicht mit dem Aufruf
panel.getGraphics().drawImage(img, 0, 0, panel);

Eine Faustregel, mal hervorgehoben: *Nie auf einer Component "getGraphics" aufrufen!* 

Alles, was gezeichnet werden soll, muss von der paintComponent-Methode aus gezeichnet werden. Wenn du ein JPanel haben willst, das ein Bild zeichnet, dann MUSST du eine eigene Klasse erstellen, die von JPanel erbt, und in der paintComponent richtig überschrieben ist. Das kann notfalls auch eine anonyme Klasse sein. 

Mal der Versuch, mit möglichst wenigen Änderungen das beschriebene zu erreichen:

```
import javax.swing.*;
import java.awt.*;

public class TestXXXX extends JFrame {

    Image img;
    JPanel panel;

    public static void main(String[] args) {
        TestXXXX t = new TestXXXX();
    }

    public TestXXXX() {
        super("test");
        Container cp = this.getContentPane();
        img = Toolkit.getDefaultToolkit().createImage(getClass().getResource("276.jpg"));
        MediaTracker mt = new MediaTracker(this);
        mt.addImage(img, 0);
        try{
            mt.waitForAll();
        }catch(InterruptedException e){}

        panel = new JPanel()
        {
            public void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                g.drawImage(img, 0, 0, this);
            }

        };
        cp.setLayout(new GridLayout());
        panel.setBounds(20, 20, 200, 200); // Bring nix, wenn ein LayoutManager verwendet wird
        cp.add(panel);

        //panel.getGraphics().drawImage(img, 0, 0, panel); BÖSE!
        setVisible(true); // setVisible immer ganz am Ende!
    }
}
```

Allerdings ist das immernoch ziemlich ... naja, das könnte man eben deutlich schöner machen...........


----------



## André Uhres (6. Aug 2009)

Shiwayari hat gesagt.:


> Also als JLabel und Icon geht das ganze. Ich bräuchte aber etwas indem sich das Bild noch skalieren lässt. Habe mal über das BackgroundPanel geschaut und ausprobiert, aber mal abgesehen davon, dass ich nicht 100%ig weiß wie das zu benutzen ist..


Die Benutzung ist einfach: pImage = new BackgroundPanel(cover);

In drawScaled sehen wir, wie skaliert wird:
Dimension d = getSize();
g.drawImage(image, 0, 0, d.width, d.height, null);

Dabei geschieht das Malen grundsätzlich in paintComponent. Siehe dazu Malen in Swing Teil 1: der grundlegende Mechanismus - Byte-Welt Wiki

Noch ein nützlicher Beitrag: Leistungsfähige scaleImage Methode + drawImage auf Cliprechteck beschränken
Selbstverständlich können wir auch einfach mit der JLabel Variante skalieren, etwa so:
pImage.setIcon(new ImageIcon(new ImageIcon(getClass().getResource(name + ".jpg")).getImage().getScaledInstance(-1, 500, 0)));
oder so:
pImage.setIcon(new ImageIcon(scaleImage(new ImageIcon(getClass().getResource(name + ".jpg")).getImage(), 1.5d)));


----------



## Shiwayari (8. Aug 2009)

So.. hab's nun mal nach Marco13's Methode probiert. Habe den betroffenen Teil des Programms in 2 Testklassen nachgebaut..:


```
import javax.swing.*;
import java.awt.*;
 
public class Test extends JFrame {
 
    public static void main(String[] args) {
        Test t = new Test();
    }
 
    public Test() {
        super("test");
        Test2 t2 = new Test2();
        Container cp = getContentPane(); 
        cp.setLayout(new GridLayout());
        cp.add(t2.panel2);
        setVisible(true);
    }
}

________________________________________________________________

import java.awt.*;
import javax.swing.*;

public class Test2 {

	private GridBagLayout gridBag = new GridBagLayout();
	private GridBagConstraints gridConst = new GridBagConstraints();		
	
    Image img;
    JPanel panel2 = new JPanel(gridBag);
    JPanel panel = new JPanel(){
        public void paintComponent(Graphics g){
            super.paintComponent(g);
            int w = img.getWidth(this);
            int h = img.getHeight(this);
            g.drawImage(img, 0, 0, 160, (160*h)/w, this);
        }
    };
	
	public Test2(){
        panel2.add(panel);
		int[] width = new int[6];
		int[] height = new int[20];
		for (int i = 0; i < 6; i++) { width[i]=60; }
		for (int i = 0; i < 20; i++) { height[i]=20; }
		img = Toolkit.getDefaultToolkit().createImage(getClass().getResource("test.jpg"));
        MediaTracker mt = new MediaTracker(panel);
        mt.addImage(img, 0);
        try{
            mt.waitForAll();
        }catch(InterruptedException e){}
        int w = img.getWidth(panel);
        int h = img.getHeight(panel);
		panel.setMinimumSize(new Dimension(160,(160*h)/w));
		panel.setPreferredSize(new Dimension(160,(160*h)/w));
		panel.setMaximumSize(new Dimension(160,(160*h)/w));
		gridBag.columnWidths=width;
		gridBag.rowHeights=height;	
		gridConst.gridx=2;
		gridConst.gridy=2;
		gridConst.gridheight=10;
		gridConst.gridwidth=2;
		gridBag.setConstraints(panel, gridConst);		
		panel2.add(panel);	
	}

}
```

und genau so in mein Programm übernommen (Konstruktor von Test2 jedoch als aufgerufene Methode) und es funktioniert nun.


Durch das Datei-Auslesen blick ich aber noch nicht ganz durch..
Wenn ich versuche ein Bild zu laden, das es im Verzeichnis nicht gibt, dann bekomme ich das hier:
	
	
	
	





```
Uncaught error fetching image:
java.lang.NullPointerException
	at sun.awt.image.URLImageSource.getConnection(Unknown Source)
	at sun.awt.image.URLImageSource.getDecoder(Unknown Source)
	at sun.awt.image.InputStreamImageSource.doFetch(Unknown Source)
	at sun.awt.image.ImageFetcher.fetchloop(Unknown Source)
	at sun.awt.image.ImageFetcher.run(Unknown Source)
```

Müsste also erst nachprüfen, ob es die Datei mit dem jeweiligen Namen überhaupt gibt.. Habe 
	
	
	
	





```
File f = new File(name+".jpg");
if(f.exists()){...}
```
probiert, aber dabei kommt immer raus, dass es die Datei nicht gibt, obwohl sie im Verzeichnis ist. Überhaupt hat das Zugreifen auf eine Datei mit relativem Pfad noch nie ohne getClass().getResource(..) funktioniert.. Habe auch mal

```
File f = new File(getClass().getResource(name+".jpg").toString());

File f = new File(getClass().getResource(name+".jpg").toFile());
```
versucht, aber da passiert genauso wenig.

Und noch eine Frage.. wenn ich die Bilder mit getClass().getResource(..) aus einem Unterverzeichnis im Quellverzeichnis auslese, kann ich dann nach dem Kompilieren trotzdem noch Bilder hinzufügen/entfernen oder werden alle Dateien dann 'im Programm' bzw. in der jar-Datei gespeichert?


----------



## André Uhres (8. Aug 2009)

Ein Bild, das mit ins jar kommt, ist immer vorhanden, eine Überprüfung somit überflüssig. Ähnlich verhält es sich bei einem Bild, das dynamisch über JFileChooser geladen wird: wenn es nicht vorhanden ist, kann man es erst gar nicht auswählen.


----------



## Shiwayari (8. Aug 2009)

Hmm, das hat mir noch nicht viel geholfen. Dass Dateien in der jar immer vorhanden sind, ist ja klar. JFileChooser kenn ich noch nicht.
Im Moment läuft das Programm so: Es werden zur Laufzeit Objekte instanziert, denen ein Name zugeordnet wird, mit dem, je nach dem ob eines vorhanden ist, ein Bild mit dem selben Namen aus einem Unterverzeichnis geladen wird, welches nach dem Kompilieren im selben Verzeichnis wie die jar-Datei liegen soll, jedoch nicht in der jar selbst. Bilder sollen dann beliebig aus dem Ordner gelöscht/hinzugefügt werden können und im Programm durch einen 'Bild laden' Button aktualisiert werden können.
Nun frage ich mich, ob das mit der getClass().getResource()-Methode geht? Wenn dadurch Dateien nur direkt aus schon in der jar-Datei vorhandenen Verzeichnissen ausgelesen werden können, dann wohl nicht. Wenn das so nicht geht, was benutze ich dann am besten? Wenn doch, wie bekomme ich es dann hin zu schauen ob eine Datei existiert oder nicht, da die Methode ja sonst scheinbar das Programm crasht.

Kenne mich da noch garnicht aus, habe bis jetzt mehr geraten was ich benutze, wüsste auch nicht nach was ich da suchen könnte. Ein paar Ansätze oder Methoden-/Klassennamen oder so wären hilfreich.


----------



## Marco13 (8. Aug 2009)

Zum eigentlichen Laden kannst du dir auch mal ImageIO (Java 2 Platform SE v1.4.2) ansehen. Da braucht man dann auch keinen MediaTracker mehr. 

Bezüglich der relativen Pfade: Wenn man NICHT mit getResource arbeitet, kann man mit 
"/datei.jpg" bzw. "./datei.jpg" auf verschiedene (d.h. auch übergeordnete) Verzeichnisse verweisen.


----------



## André Uhres (9. Aug 2009)

Shiwayari hat gesagt.:


> JFileChooser kenn ich noch nicht.




```
public void loadImage() {
    File picturesDir = new File(System.getProperty("user.dir") + "/pictures");
    picturesDir.mkdir();
    JFileChooser fileChooser = new JFileChooser(picturesDir);
    switch (fileChooser.showOpenDialog(null)) {
        case JFileChooser.APPROVE_OPTION:
            try {
                image = ImageIO.read(fileChooser.getSelectedFile());
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            break;
        case JFileChooser.CANCEL_OPTION:
            break;
    }
}
```


----------



## Shiwayari (9. Aug 2009)

Habe etwas mit den relativen Pfaden rumprobiert und gemerkt, dass es daran liegt, dass ich eigenes Package habe.

```
cover = ImageIO.read(new File("shiwayari/adb/covers/"+name+".jpg"));
```
geht nun, aber nur vor dem packen in eine jar..
Habe den Ordner "covers" im selben Order wie die jar, und gleichzeitig noch in der jar  unter "adb.jar/shiwayari/adb/covers/", aber aus der jar soll es ja sowieso nicht gelesen werden.
Habe auch 

```
cover = ImageIO.read(new File("./shiwayari/adb/covers/"+name+".jpg"));
```
probiert, das funktioniert auch ausserhalb der jar (obwohl der Ordner -nicht- übergeordnet ist?), nach dem packen nicht.

Der FileChooser funktioniert dagegen immer und ist ja ziemlich einfach zu benutzen.
Nur bin ich mir nicht so sicher, ob das später noch so funktioniert wie gewollt.
Die Bilder sollen sowieso alle den passenden Namen haben, dann lade ich lieber alle automatisch, als 'umständlich' jedes Bild über ein extra Fenster selbst auszuwählen.

Also, wie komme ich mit dem Dateipfad in einen Ordner, der im selben Ordner wie die jar liegt?
Wenn das dann immer noch nicht gehen will, gibt es vielleicht eine Moeglichkeit den absoluten Pfad der jar rauszubekommen? Dann könnte ich den einfach als String speichern und beim auslesen absolute Pfade benutzen, das geht wenigstens.

Edit____________________________________________________________

Habe was passendes gefunden:

```
String absPath = new File("").getAbsolutePath();
cover = ImageIO.read(new File(absPath+"\\covers\\"+name+".jpg"));
```
Genau was ich brauche, und schön kurz. Damit wäre hier erstmal alles gelöst.
Danke für eure Hilfe


----------

