# JPanel neu zeichnen mit repaint



## Guest (14. Jan 2009)

Folgendes Problem... Ich hab ein Tabbed Pane. Jedesmal wenn ich einen bestimmten Tab anklicke wird ein bestimmtes Panel angezeigt. Panel A enthält einen Button. Wenn ich diesen klicke soll ein Label dem Panel hinzugefügt werden.
Ich nutze also die Methode p.add(new JPanel("Test")); Natürlich wird das so veränderte Panel nicht sofort angezeigt. Aber auch ein repaint ändert da nix dran. Wenn ich jedoch in dem tabbed pane auf einen anderen tab klicke und zu meinem modifizierten zurückkehre, wird das Label bzw. die Labels wenn ich mehrmals geklickt habe, angezeigt. Da klappt es doch auch...

Ich skiziere mal den (Pseudo)Code...

Tabbed Pane u.a.


```
...
private p;
this.addTab("Tab1", getJPanel_Tab1());
this.addTab("Tab2", getJPanel_Tab1());
...
```

In der Methode getJPanel_Tab1


```
...
Button b = ...
b.addListener(
    // im listener p verändern
   p.add(new JLabel("test");
   ); 

p = new JPanel()
p.add(b)
...
// nun wird bei klick auf button im panel diesem panel was in der tabbed pane klasse ein field ist
// ein jlabel hinzugefügt
```

Wie gesagt, wenn ich zwischen den Tabs wechsle, wird dann auch das Label/die Label welche(s) ich durch Buttonklick
erzeugt habe angezeigt.

Auch ein hinzufügen 

In der Methode getJPanel_Tab1


```
...
Button b = ...
b.addListener(
    // im listener p verändern
   p.add(new JLabel("test");
   ); 

p = new JPanel()
p.add(b)
```
*p.repaint()*

```
...
// nun wird bei klick auf button im panel diesem panel was in der tabbed pane klasse ein field ist
// ein jlabel hinzugefügt
```

ändert daran nichts. Hat wer eine Idee?


----------



## L-ectron-X (14. Jan 2009)

Wenn du zur Laufzeit GUI-Komponeten hinzufügst, musst du danach validate() aufrufen und *nicht* repaint().


----------



## Gast (15. Jan 2009)

Hallo,

danke für die Antwort... Ja, das klappt. Aber die Frage: Wo liegt da der Unterschied? D.h. was nützt dann repaint?


----------



## Ebenius (15. Jan 2009)

Laut API-Doc:

public void repaint() : Repaints this component.
public void validate() : Ensures that this component has a valid layout. This method is primarily intended to operate on instances of Container.

Repaint ist also das einfache Neuzeichnen (zum Beispiel, wenn Du die Hintergrundfarbe änderst, wird automatisch repaint() benutzt). Validate wird benutzt, wenn sich das Layout (also die Aufteilung der Komponenten) ändert. Wenn die Komponente invalid ist (siehe invalidate()) wird nach dem nächsten validate()-Aufruf das Layout neu aufgebaut.

Hilft das? Grüße, Ebenius


----------



## Verjigorm (15. Jan 2009)

Wenn du Layout veränderst, also irgendwelche Komponenten hinzufügst/entfernst, musst du dem Layoutmamager sagen, dass er sich bemühen muss -> (re)validate() (=neuberechnen) aufrufen
das ruft auch repaint() auf

Wenn du nur "zeichentechnisch" was änderst, ohne das Layout zu verändern, dann repaint() (=neuzeichnen)

edit: zu spät


----------



## Gast (17. Jan 2009)

Hallo,

danke für die Antworten. Wenn ich validate aufrufe und keine Komponente hinzugefügt/entfernt habe, dann entfällt auch das "repaint", oder? D.h. repaint erfolgt auch nur, wenn wirklich Komponenten hinzugefügt wurden, also Änderungen am Layoutmanager stattfanden...???


----------



## Ebenius (17. Jan 2009)

Nö. Repaint erfolgt trotzdem.


----------



## Gast (17. Jan 2009)

Hmm,

ok. wieso reicht es dann aber nicht, wenn ich z.B. einem JPanel per "add" eine Komponente hinzugefügt habe nur "repaint" aufzurufen (zur Laufzeit)? DIe Komponenten werden erst sichtbar, wenn ich validate aufgerufen habe.


----------



## Gast (17. Jan 2009)

Nachtrag...

Ich entferne in einer Schleife mittels "remove" Komponenten aus einem JPanel. Wenn ich nach der Schleife, d.h. nach dem Entfernen der Komponenten,  ein "repaint" aufrufe, sieht die GUI "richtig" aus. Wenn ich jedoch nur ein "validate" aufrufe, bleibt eine entfernte Komponente noch sichtbar. Das ist etwas komisch


----------



## Ebenius (17. Jan 2009)

Erst validate() dann repaint(). Damit funktioniert's wie's soll.


----------



## André Uhres (17. Jan 2009)

Gast hat gesagt.:
			
		

> Ich entferne in einer Schleife mittels "remove" Komponenten aus einem JPanel. Wenn ich nach der Schleife, d.h. nach dem Entfernen der Komponenten,  ein "repaint" aufrufe, sieht die GUI "richtig" aus. Wenn ich jedoch nur ein "validate" aufrufe, bleibt eine entfernte Komponente noch sichtbar.


Es funktioniert auch mit :

panel.revalidate();
panel.repaint();

Das wäre effizienter weil nur der parent Container neu validiert und gemalt wird.

Wenn wir es auf JFrame Ebene machen, dann wird unnötige Arbeit erledigt.


----------



## L-ectron-X (17. Jan 2009)

Gast hat gesagt.:
			
		

> ok. wieso reicht es dann aber nicht, wenn ich z.B. einem JPanel per "add" eine Komponente hinzugefügt habe nur "repaint" aufzurufen (zur Laufzeit)? DIe Komponenten werden erst sichtbar, wenn ich validate aufgerufen habe.


Der LayoutManager wird vor dem Neuzeichnen noch beauftragt, die Komponenten und die Platzverhältnisse neu zu berechnen, ehe gezeichnet wird.


----------



## André Uhres (18. Jan 2009)

Was L-ectron-X gesagt hat, ist auch der Grund, weshalb bei remove ein revalidate oft nicht genügt. 
Im Endeffekt wird nämlich nicht validiert und gemalt, da sich für das Layout nichts Relevantes geändert hat.


----------



## Gast (18. Jan 2009)

Hmm, aber warum das in der Schleife nicht klappt hab ich noch immer nicht ganz verstanden


----------



## Ebenius (18. Jan 2009)

Ich auch nicht. :-D


----------



## André Uhres (18. Jan 2009)

Nehmen wir als Beispiel ein JPanel mit zwei JLabels und FlowLayout(CENTER).

Wenn wir jetzt das JLabel rechts entfernen, dann muss der Layoutmanager das andere neu zentrieren,
also müssen wir panel.revalidate() aufrufen, damit er das tut. Weil die Komponenten aber nicht neu geordnet 
werden müssen, sieht das System (leider) keine direkte Notwendigkeit, repaint() aufzurufen. 
Darum müssen wir auch noch panel.repaint() aufrufen.

Wenn ich jetzt das JLabel links entferne, dann muss der Layoutmanager das andere ebenfalls 
neu zentrieren, also müssen wir ebenfalls panel.revalidate() aufrufen. Weil die Komponenten 
jetzt neu geordnet werden müssen (ein JLabel rückt eine Stelle nach links) ruft das System 
automatisch repaint() auf. Darum brauchen wir in diesem Fall panel.repaint() nicht aufzurufen.

Bei FlowLayout(LEFT) würde im ersten Fall sogar ein panel.repaint() genügen, weil das
andere JLabel schon an der richtigen Stelle steht.

Es hängt also viel vom verwendeten Layoutmanager ab und auch von der relativen Position 
der gelöschten Komponente(n).

Um allen Problemen aus dem Weg zu gehen, empfehlen wir bei remove 
sowohl panel.revalidate() als auch panel.repaint() aufzurufen.

EDIT: Das ist jetzt aber etwas vereinfacht ausgedrückt. Wenn neu zentriert wird, wird vom System
im ersten Fall natürlich auch repaint aufgerufen, aber nur für den Bereich bis zur gelöschten
Komponente: also nicht panel.repaint() sonder ein repaint(..) mit den Argumenten, die
das zu malende Rechteck definieren.


----------

