Wer oder was ruft die "paintComponent()" Methode auf?

Jack159

Bekanntes Mitglied
Hallo,

Ich arbeite mich gerade in die GUI Programmierung ein.
Hier gehts um ein Beispielprogramm, bei dem ein Rechteck auf ein JPanel gezeichnet wird. Der Code steht unten.

Soweit verstehe ich davon alles. Mir ist nur nicht klar, wie bzw. warum die "paintComponent" Methode aus der Klasse "MeinZeichenPanel" aufgerufen wird?!
Die ganze Action spielt sich ja in der "EinfacheGui" Klasse ab. Dort wird das Frame und Panel erstellt usw.
Aber dort wird ja nirgends die "paintComponent" Methode aus der anderen Klasse aufgerufen. Zumindest verstehe ich nicht, wie sie dort aufgerufen wird (Da das Rechteck ja gezeichnet wird, muss sie aufgerufen werden 😀 ).
Ich weiß, dass ein Objekt der Klasse "MeinZeichenPanel" erstellt wird, aber das ruft doch nicht die "paintComponent" Methode auf?! ???:L

Java:
import java.awt.BorderLayout;

import javax.swing.JFrame;

public class EinfacheGui {

	JFrame frame;
	
	public static void main(String[] args) {
		EinfacheGui gui = new EinfacheGui();
		gui.los();
	}
	
	public void los() {
		frame = new JFrame();
		frame.setVisible(true);
		frame.setSize(900, 900);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		MeinZeichenPanel zeichenPanel = new MeinZeichenPanel();
		
		frame.getContentPane().add(BorderLayout.CENTER, zeichenPanel);
		
	}
	
}

Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Rectangle2D.Double;

import javax.swing.JPanel;

public class MeinZeichenPanel extends JPanel {

	public void paintComponent(Graphics g) {
		Graphics2D g2d= (Graphics2D) g;
		Double rectangle = new Rectangle2D.Double(200, 120, 70, 30);
		
		g2d.setPaint(Color.red);
		g2d.fill(rectangle);
		
		
	}
	
}
 
Diese Methode wird vom EDT (Event Dispatching Thread) aufgerufen, sobald sich irgendwas an deinem Fenster verändert (z.B. durch Resize) oder wenn du
Code:
Component#repaint()
aufrufst. Genaugenommen wird dann erstmal
Code:
paint()
aufgerufen, welches widerum einige andere Methoden aufruft und dir im Ergebnis das eigtl. interessante Graphics-Object g zurückgibt, Das kannst du dann nutzen, um zu zeichnen.
Wo dieses Objekt tatsächlich herkommt, brauch dich gar nicht zu interessieren, es ist einfach genau da, wo du´s brauchst^^ (ich hab das aber auch ewig nicht verstanden...), aktualisiert sich also auch von selbst...
 
Zuletzt bearbeitet:
Das habe ich auch verstanden. Aber was ich nicht verstehe ist, wozu

Java:
 MeinZeichenPanel zeichenPanel = new MeinZeichenPanel();

gut sein soll.
 
Naja, das ist doch wie mit jedem anderen Objekt jeder anderen Klasse auch so, du musst erst eine Instanz deklarieren (z.B. mit
Code:
ZeichenOanel zeichenPanel;
) und sie dann instanziieren (z.B.
Code:
zeichenPanel = new ZeichenPanel()
oder
Code:
zeichenPanel = this.getZeicheninstanz()
oder...). Oder wie in deinem Fall: du machst beides in einer Zeile:
Java:
ZeichenOanel zP = new ZeichenPanel();
Damit erstellst du also in deinem Fenster (Frame) ein neues Panel... wo ist das Problem?
 
Naja, das ist doch wie mit jedem anderen Objekt jeder anderen Klasse auch so, du musst erst eine Instanz deklarieren (z.B. mit
Code:
ZeichenOanel zeichenPanel;
) und sie dann instanziieren (z.B.
Code:
zeichenPanel = new ZeichenPanel()
oder
Code:
zeichenPanel = this.getZeicheninstanz()
oder...). Oder wie in deinem Fall: du machst beides in einer Zeile:
Java:
ZeichenOanel zP = new ZeichenPanel();
Damit erstellst du also in deinem Fenster (Frame) ein neues Panel... wo ist das Problem?

Dass ich damit ein Objekt (Panel) erzeuge, ist mir klar.
Ich verstehe nur immer noch nicht, wie allein die erzeugung des Objektes
Java:
MeinZeichenPanel zeichenPanel = new MeinZeichenPanel();
die paintComponent der Klasse MeinZeichenPanel aufruft?!
Wenn der paintComponent Code z.B. im Konstruktor der Klasse "MeinZeichenPanel" stehen würde, dann würde ich es ja verstehen, aber so ???:L
 
Achso... naja, das ist so, wie wenn du ein Fenster initialisierst und erstmalig anzeigst. Dafür wird erstmal ein Component-resize-Erreignis abgefeuert, das gleiche passiert beim Panel.

EDIT: Aber die Komponente soll ja u.U. nicht nur beim Erzeugen gezeichnet werden, sondern (wie ich bereits erwähnte) auch, wenn du dein Fenster vergrößerst oder verschiebst, jedes Mal muss dabei der GraphikKontext neu berechnet werden (also der Ausschnitt deines Bildschirms, in den du zeichnest).
 
Zuletzt bearbeitet:
Achso... naja, das ist so, wie wenn du ein Fenster initialisierst und erstmalig anzeigst. Dafür wird erstmal ein Component-resize-Erreignis abgefeuert, das gleiche passiert beim Panel.

EDIT: Aber die Komponente soll ja u.U. nicht nur beim Erzeugen gezeichnet werden, sondern (wie ich bereits erwähnte) auch, wenn du dein Fenster vergrößerst oder verschiebst, jedes Mal muss dabei der GraphikKontext neu berechnet werden (also der Ausschnitt deines Bildschirms, in den du zeichnest).

Ich bin dir dankbar für deine Hilfe, aber ich verstehe es immer noch nicht 🙁
Ich habe verstanden, dass repaint() aufgerufen wird, sobald das Fenster verschoben wird etc.

Ich verstehe wie gesagt immer noch nicht, wie die PaintComponent Methode beim Start aufgerufen wird, bzw. was das "zeichenPanel" Objekt damit zu tun haben soll.
Ok vielleicht wird PaintComponent beim Start aufgerufen, weil das Fenster eben erzeugt wird, aber was hat das Objekt "zeichenPanel" damit zu tun?

Java:
zeichenPanel.paintComponent(g);
fänd ich z.b. logisch sinnvoll in der main-Methode, wird aber als Fehler angezeigt...
 
paintComponent() wird niemals von dir selbst aufgerufen (bestenfalls wie schon erwähnt repaint(), welches paintComponent dann seinerseits aufruft).
Erstmalig wird paintComponent() aufgerufen, wenn das zugehörige Objekt SICHTBAR wird (womit ein Resize-Ereignis abgefeuert wird, denn es ist ja von Größe 0 auf Größe x*y angewachsen). Aber wie bereits gesagt: in vielen Fällen brauchst du dich gar nicht darum kümmern, diese Funktion aufzurufen, sie ist halt einfach da und du kannst sie nutzen 😀

EDIT: Was kommt da für ein Fehler? Laufzeitfehler höchstens, weil du g nicht richtig befüllt hast (was überdies auch ziemlich umständlich bis unmöglich ist).
 
paintComponent() wird niemals von dir selbst aufgerufen (bestenfalls wie schon erwähnt repaint(), welches paintComponent dann seinerseits aufruft).
Erstmalig wird paintComponent() aufgerufen, wenn das zugehörige Objekt SICHTBAR wird (womit ein Resize-Ereignis abgefeuert wird, denn es ist ja von Größe 0 auf Größe x*y angewachsen). Aber wie bereits gesagt: in vielen Fällen brauchst du dich gar nicht darum kümmern, diese Funktion aufzurufen, sie ist halt einfach da und du kannst sie nutzen 😀

Ok verstanden, aber wozu brauche ich dann

Java:
MeinZeichenPanel zeichenPanel = new MeinZeichenPanel();
? Wozu ist das gut? Was macht dieses Objekt?
 
Dein rotes Rechteck zeichnen? OK, in deinem Bsp. könntest du auch in den Kontext des Fensters zeichnen, das sollte keinen Unterschied machen. Aber was ist, wenn du noch paar Buttons und was weiß ich nicht noch alles da hast (welche ebenfalls IN den Kontext des Fensters gezeichnet werden). Wenn du dann paintComponentn von dem Fenster überschreibst, werden deine ganzen anderen Steuerelemente u.U. nicht mehr gezeichnet...

EDIT: Du kannst ja mal eine
Code:
System.out.println("paintComponent aufgerufen")
; machen, um zu sehen, wie oft die Methode aufgerufen wird...
 
Zuletzt bearbeitet:
Dein rotes Rechteck zeichnen? OK, in deinem Bsp. könntest du auch in den Kontext des Fensters zeichnen, das sollte keinen Unterschied machen. Aber was ist, wenn du noch paar Buttons und was weiß ich nicht noch alles da hast (welche ebenfalls IN den Kontext des Fensters gezeichnet werden). Wenn du dann paintComponentn von dem Fenster überschreibst, werden deine ganzen anderen Steuerelemente u.U. nicht mehr gezeichnet...

EDIT: Du kannst ja mal eine
Code:
System.out.println("paintComponent aufgerufen")
; machen, um zu sehen, wie oft die Methode aufgerufen wird...

Wie zeichnet zeichenPanel denn das Rechteck? zeichenPanel hat doch garkeine Methoden usw, es ist doch nur ein leeres Objekt?!
 
Hier mal ein neues Beispiel, damit du/ihr besser versteht, wo mein Verständnissproblem liegt:

Java:
public class Eins {

	public static void main(String[] args) {
		Eins gui = new Eins();
		gui.los();
		
	}
	
	public void los() {
		
		Zwei zeichenPanel = new Zwei();
		
	}

Java:
public class Zwei {
	
	public void ausgabe() {
		
		System.out.print("Hallo Welt");
		
	}

}

Beim Start des programms passiert nichts. Es wird nichts ausgeben.

Wenn ich jetzt nach der Logik von dem Programm aus meinem Startpost gehe, dann müsste in diesem Programm hier der Text "Hallo Welt" ausgegeben werden bzw. die Methode ausgabe() aufgeruft werden, NUR weil ich ein Objekt von "Zwei" erzeugt habe.
Das Beispiel hier ist ja im Prinzip nichts anderes. Und da liegt mein Verständnissproblem...

Oben wird auch nur ein Objekt erzeugt, was aber zur Folge hat, dass paintComponent ausgeführt wird.
Hier wird ebenfalls nur ein Objekt erzeugt, aber ausgabe() wird nicht ausgeführt....
 
Da hast du aber anständig was missverstanden. Die Methode ausgabe() wird nie aufegrufen, wie du ja an dem fehlenden sysout festgestellt hast. Mit paintComponent hat das Ganze aber rein gar nichts zu tun, den paintComponent setzt - wie der Name bereits andeutet - eine Instanz der Klasse Component voraus (oder einer Subklasse), was in deinem Fall aber nicht zutrifft. Der automatische Aufruf von paintomponent kommt daher, dass irgendwann in der langen Hierarchieebene der Komponenten (früher oder später wird auf jeden fall der Konstruktor von Component ausgeführt, ob du willst oder nicht^^) im Konstruktor repaint() aufgerufen wird (das ist zwar alles nicht wortwörtlich richtig, aber kommt sinngemäß trotzdem hin, zumindest in diesem Zusammenhang... egal). Da weder Eins noch Zwei jedoch auch nur irgendwas mit einer JComponent zu tun haben (was in einer Konsolenanwendung aber sowieso auch nicht geht), kann da auch nichts weiter passieren...
paintComponent gibt´s nur, wenn du eine GUI schreibst (genauer: nur in Swing, in AWT gibts "bloß" paint())
 
ein weiterer Versuch....:

Wenn wir in deinem Bsp mit den Klassen [c]Eins und Zwei[/c] einen Konstruktor in Klasse Zwei definieren, der nichts weiter macht, als die Methode [c]ausgabe()[/c] aufzurufen.

Java:
class Zwei  {
	public Zwei() {
		ausgabe();
	}

	public void ausgabe() {

		System.out.print("Hallo Welt");

	}

}

dann erhälst du auch den Konsolenausdruck.
Ähnlich, nur nicht ganz so trivial, verhält es sich bei der
Code:
paintComponent(Graphics g)
Methode.
Die wird allerdings nicht nur einmal, sondern zig male aufgerufen,
 
Zuletzt bearbeitet:
Die Swing-GUI-Komponenten sind Event-Quellen. Event-Quellen sind Objekte die Benutzeraktionen ausführen(Mouseklick, Taste, Fenster).

Wenn du mit GUI arbeitest funktioniert das meiste im Hintergrund, also wird vom System aufgerufen.

Dazu kommt wie man Sachen(Grafiken) auf den Bildschirm platziert.
Normalerweise wird ein Fenster[c]frame[/c] erstelllt das ein ZeichenPanel[c]JPanel[/c] enthält.
In dem JPanel wird die Grafik erstellt.
Die Grafik ensteht durch die Methode [c]paintComponent()[/c] die ja in einer Klasse steht die [c]JPanel[/c] erweitert!

Die [c]paintComponent()[/c] rufst du nie selber auf! Das Argument dieser Methode [c]g[/c](ein Objekt vom Typ Graphics) ist die eigentliche Malwand, an dies kommst du selber nicht dran, es wird vom [c]System[/c] gegeben. Du kannst jedoch das System drum bitten die Anzeige "aufzufrischen" in dem die Methode [c]repaint()[/c] aufgerufen wird, was dann [c]paintComponent()[/c] aufruft.

Es gibt Polymorphie die innerhalb des Systems funktioniert, und solche die du selber schreibst.
 
ein weiterer Versuch....:

Wenn wir in deinem Bsp mit den Klassen [c]Eins und Zwei[/c] einen Konstruktor in Klasse Zwei definieren, der nichts weiter macht, als die Methode [c]ausgabe()[/c] aufzurufen.

Java:
class Zwei  {
	public Zwei() {
		ausgabe();
	}

	public void ausgabe() {

		System.out.print("Hallo Welt");

	}

}

dann erhälst du auch den Konsolenausdruck.
Ähnlich, nur nicht ganz so trivial, verhält es sich bei der
Code:
paintComponent(Graphics g)
Methode.
Die wird allerdings nicht nur einmal, sondern zig male aufgerufen,

Ich glaube jetzt könnte ich es verstanden haben 😀

Da ich ja ein Objekt von einer Unterklasse von JPanel erzeuge, wird ja auch der Konstruktor von JPanel aufgerufen.
Hab mal in der API nachgeschaut, im JPanel Konstruktor steht nirgends repaint(). Allerdings wird im JPanel Konstruktor ein neues JPanel erzeugt. Und da ein JPanel ja etwas mit Grafik/Grafikveränderung zu tun hat, wird also die repaint() Methode (Und damit auch dann die paintComponent()) Methode aufgerufen?! Denn repaint() wird ja sofort aufgerufen, sobald man das Fenster in irgendeiner Weise verändert.

Ist das so in etwa richtig?
 
Ich glaube jetzt könnte ich es verstanden haben 😀

Da ich ja ein Objekt von einer Unterklasse von JPanel erzeuge, wird ja auch der Konstruktor von JPanel aufgerufen.
Hab mal in der API nachgeschaut, im JPanel Konstruktor steht nirgends repaint(). Allerdings wird im JPanel Konstruktor ein neues JPanel erzeugt. Und da ein JPanel ja etwas mit Grafik/Grafikveränderung zu tun hat, wird also die repaint() Methode (Und damit auch dann die paintComponent()) Methode aufgerufen?! Denn repaint() wird ja sofort aufgerufen, sobald man das Fenster in irgendeiner Weise verändert.

Ist das so in etwa richtig?
nicht ganz... im Konstruktor von JPanel wird kein weiteres JPanel erstellt.... aber es wird die updateUI methode aufgerufen, die schlussendlich in der setUI von JComponent landet (Vaterklasse von JPanel), und die ruft ein repaint() auf.

wahrscheinlich hat dich das [c]this(...)[/c] in den konstruktoraufrufen verwirrt. Hier wird kein weiteres Objekt erzeugt, sondern einfach ein anderer Konstruktor aufgerufen - sozusagen eine weiterleitung der Anforderung "erstelle bitte das objekt JPanel"
 

Zurück
Oben