# SWING Fenster mit mehreren JPanels (dank JLayeredPane)



## javaianer (29. Mai 2009)

Ich möchte ein Swing Fenster (JFrame) mit Hintergrundbild haben. Dieses Hintergrundbild soll teilweise von einem Bereich (JPanel) verdeckt werden, auf dem die Methode paint zu nutzen ist.

Bisher habe ich es hinbekommen, ein JFrame mit Hintergrundbild zu erzeugen, welches von contentPane.add("bild.jpg") verdeckt wird. Aber ich bekomme es einfach nicht hin, dass auch ein JPanel das Hintergrundbild verdeckt.

Schon mal vielen dank!

(Ich weiss es ist knapp beschrieben, aber ich hätte einfach gerne die einfachste Möglichkeit.
Hintergrund, was ich damit anfangen möchte:
Ein Spiel soll ein Hintergrund haben und auf diesem soll ein Bereich sein, mit schwarzem Hintergrund auf dem über die paint Methode gezeichnet wird.
Mich interessieren keine Listener, sondern einfach nur die Visualisierung.)


----------



## André Uhres (29. Mai 2009)

Das Bild:

```
JLabel contentPane = new JLabel();
ImageIcon backgroundImage = null;
try {
    backgroundImage = new ImageIcon(ImageIO.read(getClass().getResource("/images/bubbles.gif")));
} catch (IOException ex) {
    ex.printStackTrace();
}
contentPane.setIcon(backgroundImage);
frame.setContentPane(contentPane);
```

Das JPanel:

```
JPanel p = new JPanel(){
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());
    }
};
p.setPreferredSize(new Dimension(200,200));
contentPane.setLayout(new FlowLayout());
contentPane.add(p);
```

Siehe auch: Background Panel  Java Tips Weblog


----------



## javaianer (29. Mai 2009)

Danke. Aber ich habe gelesen, dass man ImageIcon für Icons an sich verwenden sollte und beim Ausgeben von Bildern Image. Da das fürs Studium ist, würde ich das daher auch gerne mit Image machen.

Wäre es nicht eigentlich am schönsten, wenn ein JFrame erzeugt werden würde. Dann JPanels auf denen Bilder gezeichnet werden und die JPanels dann auf die ContentPane gepackt werden würden? Ich versuch das grad, aber irgendwie zeigt er mir nicht mein Bild an.


----------



## javaianer (29. Mai 2009)

Mir ist eigentlich immer noch unklar, auf welche JComponents man überhaupt malen kann und wofür welche Componente ist.

Ist das richtig, dass man auf jeder Klasse die von Jcomponent erbt malen kann?


----------



## javaianer (29. Mai 2009)

Es kann auch sein, dass ich was beim zeichnen des Bildes falsch mache. Ich versuche es so zu machen:

Image img = Toolkit.getDefaultToolkit.createImage("bild.JPG");

dann in

protected paintComponent(Graphics g) {
g.drawImage(img, anfangsPunktX, anfangsPunktY, this);

this ist doch ein ImageObserver oder?


----------



## javaianer (29. Mai 2009)

Ich bin etwas weiter gekommen, wenn ich alles in einer Klasse mache geht es und zwar mit folgenden Mitteln:
Image img = Toolkit.getDefaultToolkit().getImage(bild.JPG);
(Image img = Toolkit.getDefaultToolkit().getImage(bild.JPG); ) geht auch, aber für mich ist getImage besser!

Mit folgendem wird das geladene Bild gezeichnet:
public void paintComponent(Graphics g) {
    	super.paintComponent(g);
    	g.drawImage(img, 0, 0, this);
}

Erzeugt wird:
JFrame myFrame = new JFrame();
Container myContainer = myFrame.getContentPane();
ContentPane.add(Objekt des JPanels, mit dem zuzeichnenden Bild)

Aber wenn ich das nun aufsplitte in drei Klassen;
eine Testklasse
eine Klasse, die von JFrame erbt
eine Klasse, die von JPanel erbt
geht es nicht mehr.

An welcher Stelle muss denn JFrame auf visible geschaltet werden?


----------



## javaianer (29. Mai 2009)

Hab das Problem gefunden, lag an dem LayoutManager.


----------



## javaianer (29. Mai 2009)

Kann man mehrere Jpanels aufeinanderlegen? SO dass wenn das untere Größere ist als die darüber immer noch ein Teil von dem darunter zu sehen ist?


----------



## javaianer (29. Mai 2009)

Hab grad einen identischen Thread gefunden:
http://www.java-forum.org/awt-swing...lweise-durch-weiteres-jpanel-ueberdecken.html

Also geht das nur mit LayeredPane und nicht mit ContentPane?


----------



## javaianer (29. Mai 2009)

Das hier speziell der 2te Eintrag hat mir sehr weiter geholfen.
http://www.java-forum.org/awt-swing-swt/42142-jlayeredpane.html

Wenn es damit nun auch möglich ist auf unterste Eben ein Bild zu malen, bin ich ein gutes Stück weiter.

Wenn ich das so weit habe, mach ich daraus ein einfaches Tutorial um in einem JFrame Fenster Hintergrund und weitere Bilder darzustellen.


----------



## javaianer (29. Mai 2009)

So ich habe es nun meiner Meinung nach so simpel wie möglich gelöst. (Würde daraus gerne eine Hilfe für jedermann machen, daher schaut euch mal die 4 kleinen Klassen an und schreibt eure Anmerkungen und Kritiken. Speziell, denke ich, dass der Konstruktor zu voll ist und da einiges von besser in einer Methode aufgehoben wäre. Wenn dann das "perfekt" ist eröffne ich einen neuen Thread)

Hiermit ist es möglich auf einem Hintergrundbild alles weitere was eine GUI haben kann drauf zu packen.

Test-Klasse

```
public class TestWindow {

	public TestWindow() {
	}

	public static void main(String[] args) {
		//JFrame wird erst zum Schluß sichtbar gemacht!
		new MainWindow().setVisible(true);

	}

}
```

Hauptfenster-Klasse


```
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class MainWindow extends JFrame {
	
	JLayeredPane layeredPane;
    JPanel background, panelOne;
    
    Integer layerOne = JLayeredPane.FRAME_CONTENT_LAYER + 1;
    //Für weitere Layer (Ebenen)
    //Integer layerTwo = layerOne + 1;

	public MainWindow() {
		super("Endlich Hintergrund mit Inhalt");
		setSize(1024, 768);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
        background = new Picture("Hintergrund.JPG", 1024, 768);
        background.setBounds(0, 0, 1024, 768);
		
		panelOne = new Field();
		//Position dieses Panels
        panelOne.setBounds(358, 76, 651, 651);
       
        layeredPane = getLayeredPane();
        //JPanel mit Inhalt wird auf die LayeredPane gepackt
        layeredPane.add(panelOne, layerOne);       

        //JPanel mit dem Hintergrund wird auf die ContentPane gepackt
        add(background);
	}

}
```
Klasse um ein Bild auf einem JPanel darzustellen


```
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class Picture extends JPanel {
    
	Image img;
	int width;
	int height;
	
    public Picture(String imageName, int width, int height) {
    	img = Toolkit.getDefaultToolkit().getImage(imageName);
    	this.width = width;
    	this.height = height;
    }
    
    public void paintComponent(Graphics g) {
    	super.paintComponent(g);
    	g.drawImage(img, 0, 0, this);
    }
}
```
Eine Klasse, mit weiterem Inhalt


```
import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JPanel;

public class Field extends JPanel {    

    public Field() {
        setBackground(Color.BLACK);
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.GREEN);
        g.fillRect(100, 100, 500, 25);        
    }
    
}
```


----------



## javaianer (29. Mai 2009)

Hier nochmal das ganze aber halt mit Sachen aus dem Konstruktor in eigener Methode.


Test-Klasse

```
public class TestWindow {

	public TestWindow() {
	}

	public static void main(String[] args) {
		//JFrame wird erst zum Schluß sichtbar gemacht!
		new MainWindow().setVisible(true);

	}

}
```

Hauptfenster-Klasse


```
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class MainWindow extends JFrame {

	public MainWindow() {
		super("Endlich Hintergrund mit Inhalt");
		setSize(1024, 768);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		createContext();
	}
	
	void createContext() {
        Integer layerOne = JLayeredPane.FRAME_CONTENT_LAYER + 1; //Kann mir jemand dieses + 1 erklären?
        //Für weitere Layer (Ebenen)
        //Integer layerTwo = layerOne + 1; 

	JPanel background = new Picture("Hintergrund.JPG", 1024, 768);
    	background.setBounds(0, 0, 1024, 768);
	
    	JPanel panelOne = new Field();
    	//Position dieses Panels
    	panelOne.setBounds(358, 76, 651, 651);
   
    	JLayeredPane layeredPane = getLayeredPane();
    	//JPanel mit Inhalt wird auf die LayeredPane gepackt
    	layeredPane.add(panelOne, layerOne);       

    	//JPanel mit dem Hintergrund wird auf die ContentPane gepackt
    	add(background);
	}
}
```

Klasse um ein Bild auf einem JPanel darzustellen


```
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JPanel;

public class Picture extends JPanel {
    
	Image img;
	int width;
	int height;
	
    public Picture(String imageName, int width, int height) {
    	img = Toolkit.getDefaultToolkit().getImage(imageName);
    	this.width = width;
    	this.height = height;
    }
    
    public void paintComponent(Graphics g) {
    	super.paintComponent(g);
    	g.drawImage(img, 0, 0, this);
    }
}
```

Eine Klasse, mit weiterem Inhalt


```
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;

public class Field extends JPanel {    

    public Field() {
        setBackground(Color.BLACK);
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.GREEN);
        g.fillRect(100, 100, 500, 25);        
    }
    
}
```


----------



## André Uhres (30. Mai 2009)

Auf jede JComponent kann man sowohl malen als auch Kindkomponenten hinzufügen. Ein "ImageIcon" enthält ein Image, daher der Name. Dargestellt wird aber nur das Image. Optisch gesehen besteht darum kein Unterschied zwischen ImageIcon und Image. Der einzige Unterschied liegt in der für den Betrachter unsichtbaren programmiertechnischen Schnittstelle. Die "ContentPane" ist eine von vielen möglichen Ebenen im LayeredPane. Sie liegt innerhalb der LayeredPane in der sogenannten "FRAME_CONTENT_LAYER". "layerOne" liegt also eine Ebene höher über der ContentPane. Um mehrere JPanels aufeinanderzulegen kann man das LayeredPane benutzen, aber natürlich auch die normale Containerhierarchie (wenn wir eine Kindkomponente hinzufügen, liegt sie naturgemäss über der Mutterkomponente). Mit dem "BackgroundPanel" aus obigem Link können wir Bilder skaliert, "tiled" oder in Originalgrösse malen, sowie einen nuancierten Hintergrund mit einem Paint Objekt. Jede Komponente, die zu diesem Panel hinzugefügt wird, kann, wenn man will, automatisch durchsichtig gemacht werden, damit unsere "Malerei" durchscheinen kann. Die Schnittstelle von "BackgroundPanel" schliesst folgende Methoden ein:
public void setImage(Image image)
public void setStyle(int style)    (skaliert, "tiled" oder in Originalgrösse)
public void setPaint(Paint painter)
public void setImageAlignmentX(float alignmentX)
public void setImageAlignmentY(float alignmentY)
public void setTransparentAdd(boolean isTransparentAdd)


----------



## André Uhres (31. Mai 2009)

André Uhres hat gesagt.:


> Um mehrere JPanels aufeinanderzulegen kann man ...


übrigens auch die Methode setComponentZOrder der Klasse Container benutzen, denn jede JComponent ist ein Container. Die Z-Order (dritte Dimension) bestimmt die Reihenfolge in der die Komponenten gemalt werden; die Komponente mit der höchsten Z-Order wird zuerst gemalt und die Komponente mit der niedrigsten Z-Order wird zuletzt gemalt. Wo Komponenten überlappen, wird die Komponente mit der niedrigeren Z-Order vor die Komponente mit der höheren Z-Order gemalt.


----------



## javaianer (2. Jun 2009)

Mich würde noch interessieren, ob die Lösung mit JLayeredPane eine elegante Lösung ist?!


----------



## André Uhres (3. Jun 2009)

Ich arbeite selten mit LayeredPane. Wenn man's mit der normalen Containerhierarchie und normalen LayoutManagern hinbekommt, ist das imho vorzuziehen.


----------



## javaianer (4. Jun 2009)

André Uhres hat gesagt.:


> Ich arbeite selten mit LayeredPane. Wenn man's mit der normalen Containerhierarchie und normalen LayoutManagern hinbekommt, ist das imho vorzuziehen.



Mit normaler Containerhierarchie meinst du das mit "setComponentZOrder" (ein paar Antworten von dir vorher)?

So dann mal ganz speziell so wie es aussieht. brauch ich eine Ebene für den Hintergrund alles weitere kann auf die Ebene darüber, sprich mehr Überlappungen gibt es nicht.

Und vielen dank!


----------



## André Uhres (6. Jun 2009)

Mit "setComponentZOrder" können wir die Komponenten einer einzigen Ebene, (d.h. die alle demselben Container hinzugefügt wurden), in einer bestimmten Reihenfolge zeichnen, wodurch alle möglichen Überlappungen realisierbar sind. Eine Containerhierarchie wird durch das einfache Hinzufügen von Komponenten auf verschiedenen Ebenen aufgebaut:
a.add(b);// b ist eine Ebene unter a
b.add(c);// c ist zwei Ebenen unter a
...
Die unterste Ebene wird zuletzt gemalt, ist also für den Betrachter "oben". Wir können z.B. dem Bildpanel einfach ein anderes Panel hinzufügen.


----------



## javaianer (5. Jul 2009)

Nach dem was ich gelesen habe und was ich getestet habe, geht setComponentOrderZ nur mit Componenten, aber nicht mit Container. Hierfür gibt es nur JLayeredPane.


----------



## André Uhres (5. Jul 2009)

javaianer hat gesagt.:


> Nach dem was ich gelesen habe und was ich getestet habe, geht setComponentOrderZ nur mit Componenten, aber nicht mit Container.


Swing Komponenten sind Container. Darum ist deine Aussage wohl sinnlos.


----------

