# Problem mit remove(Component comp)



## Cru (23. Sep 2010)

Hallöchen,

mit meinem ersten Post in diesem Forum, hab ich gleich ein kleines und wahrscheinlich einfach zu lösendes Problem:

Ich möchte auf einem JPanel eine Komponente durch eine andere ersetzen.

Klingt einfach - dachte ich auch ...

Bei der Übergabe eines boolean, soll bei dem JPanel das JScrollPane durch das AnzeigeTestCanvas ersetzt werden.

Hier erstmal der Code (vereinfacht):


```
public class AnzeigeJPanel extends JPanel {
	private JScrollPane scrollPane;
	private AnzeigeTestCanvas anzeigeTestCanvas;
	private AnzeigeCanvas anzeigeCanvas;
	private boolean booleanTestCanvas;

	public AnzeigeJPanel() {
		setLayout(new BorderLayout());

		setzeCanvas(booleanTestCanvas);
	}

	private void setzeCanvas(boolean booleanTestCanvas) {
		this.booleanTestCanvas = booleanTestCanvas;

		Dimension dimensionBildschirm = Toolkit.getDefaultToolkit()
				.getScreenSize();

		if (booleanTestCanvas) {
			if (scrollPane != null) {
				System.out.println("AnzeigeCanvas wird gelöscht...");
				remove(scrollPane);
				validate();
			}

			anzeigeTestCanvas = new AnzeigeTestCanvas();
			anzeigeTestCanvas.setPreferredSize(new Dimension(
					(dimensionBildschirm.width - 400),
					(dimensionBildschirm.height - 200)));
			add(anzeigeTestCanvas, BorderLayout.CENTER);

		} else {

			if (anzeigeTestCanvas != null) {
				System.out.println("AnzeigeTestCanvas wird gelöscht...");
				remove(anzeigeTestCanvas);
				validate();
			}

			scrollPane = new JScrollPane();
			anzeigeCanvas = new AnzeigeCanvas();
			scrollPane.getViewport().setView(anzeigeCanvas);
			scrollPane.setPreferredSize(new Dimension(
					(dimensionBildschirm.width - 400),
					(dimensionBildschirm.height - 200)));
			scrollPane.setMaximumSize(new Dimension(1000, 1000));
			add(scrollPane, BorderLayout.CENTER);

		}
	}


	public void setBooleanTestCanvas(boolean booleanTestCanvas) {
		this.booleanTestCanvas = booleanTestCanvas;
		setzeCanvas(booleanTestCanvas);
	}
}
```

Im Verlauf des Programmes wird die Klasse neu instantiiert und mit setBooleanTestCanvas(true); die Variable übergeben.

Was nun eigentlich geschehen soll, ist dass das JScrollPane wie gesagt ersetzt werden soll.

Ich habe verschiedene Sachen ausprobiert, den Fehler habe ich aber nicht gefunden.


Ich danke schonmal im voraus für eure Hilfe


----------



## SlaterB (23. Sep 2010)

edit: zu schnell, noch nix sinnvollles, teste jetzt mal


----------



## Cru (23. Sep 2010)

Das funktioniert natürlich nicht.

Beim Aufrufen der kompletten GUI, ist der boolean-Wert false. (1. Instanz, JScrollPane wird gesetzt).

Beim erneuten instantiieren der Klasse(2. Instanz, JScrollPane soll ersetzt werden), wird dir If-Bedingung mit true ausgewertet.


----------



## SlaterB (23. Sep 2010)

jaja,

scheint nur ein repaint zu fehlen:

```
public class Test {
    public static void main(String[] args)   {
        JFrame f = new JFrame();
        final AnzeigeJPanel p = new AnzeigeJPanel();
        f.add(p);
        JButton b = new JButton("switch");
        b.addActionListener(new ActionListener()  {
                public void actionPerformed(ActionEvent e)   {
                    p.setBooleanTestCanvas();
                }
            });
        f.add(b, BorderLayout.SOUTH);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(400, 400);
        f.setVisible(true);
    }
}

class AnzeigeJPanel   extends JPanel {
    private JScrollPane scrollPane;
    private JLabel anzeigeTestCanvas;
    private JLabel anzeigeCanvas;
    private boolean booleanTestCanvas;

    public AnzeigeJPanel()  {
        setLayout(new BorderLayout());
        setBooleanTestCanvas();
    }

    private void setzeCanvas()  {
        Dimension dimensionBildschirm = Toolkit.getDefaultToolkit().getScreenSize();

        if (booleanTestCanvas)    {
            if (scrollPane != null)     {
                System.out.println("AnzeigeCanvas wird gelöscht...");
                remove(scrollPane);
            }
            anzeigeTestCanvas = new JLabel("anzeigeTestCanvas");
            anzeigeTestCanvas.setPreferredSize(new Dimension((dimensionBildschirm.width - 400), (dimensionBildschirm.height - 200)));
            add(anzeigeTestCanvas, BorderLayout.CENTER);
        }   else  {
            if (anzeigeTestCanvas != null)    {
                System.out.println("AnzeigeTestCanvas wird gelöscht...");
                remove(anzeigeTestCanvas);
            }

            scrollPane = new JScrollPane();
            anzeigeCanvas = new JLabel("anzeigeCanvas");
            scrollPane.getViewport().setView(anzeigeCanvas);
            scrollPane.setPreferredSize(new Dimension((dimensionBildschirm.width - 400), (dimensionBildschirm.height - 200)));
            scrollPane.setMaximumSize(new Dimension(1000, 1000));
            scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

            add(scrollPane, BorderLayout.CENTER);
        }
        validate();
        repaint();
    }

    public void setBooleanTestCanvas()  {
        this.booleanTestCanvas = !this.booleanTestCanvas;
        setzeCanvas();
    }
}
```


----------



## Cru (23. Sep 2010)

Schade, aber das bringt leider keine Veränderung bei mir.

Ich übergebe den boolean-Wert in einer anderen Klasse wie folgt...


```
AnzeigeJPanel anzeigeJPanel = new AnzeigeJPanel();
		anzeigeJPanel.setBooleanTestCanvas(true);
```

Vielleicht hilft das?

Ich weiß, dass das Programm in die Richtige IF-Bedingung hineinspringt, allerdings ändert sich nichts...


----------



## SlaterB (23. Sep 2010)

das sieht so aus als wenn du an einem neuen Panel aufrufst, 
was im neuen Panel passiert ist ganz egal, entscheidend ist, was im in die GUI eingebundenen Panel passiert,

siehe mein Programm, der Button bezieht sich auf das initial erstellte p-Panel, nicht auf ein neues


----------



## Cru (23. Sep 2010)

Wunderbar, das könnte das Problem natürlich sein.

Wie übergebe ich am Besten den boolean-Wert aus der Klasse (gehört nicht zu GUI)?

Da es so ja nicht funktioniert...


----------



## SlaterB (23. Sep 2010)

lautet deine Frage
'Wie komme ich von einer anderen Klasse aus an das richtige AnzeigeJPanel-Objekt um daran die Methode aufzurufen?'
?
Antwort: auf sinnvolle Weise

dieses Problem gibt es immer und überall, was man nicht kennt, kann man nicht bearbeiten,
entweder man (als Objekt anderer Klasse) hat initial eine Referenz bekommen oder es gibt eine unsaubere globale statische Referenz oder man kennt jemand, den man nach einer Referenz fragen kann, entweder direkt oder als Kette:
getHauptController().getHauptGUI().getXYFester().getAnzeigeJPanel()


----------



## Cru (23. Sep 2010)

Ok, vielen Dank schonmal!

Ich möchte das ganze natürlich nicht statisch haben.

Das AnzeigeJPanel liegt in einem weiteren JPanel(Oberflaeche) welches dann in einem JFrame ist.

Nun die Klasse die eben die Übergabe des boolean-Wertes durchführt hat, wie du richtig vermutet hast überhaupt nichts mit der GUI zu tun.

Also würde das evt mit einer solchen Verkettung gehen? Ich hab bloß keine Ahnung wie, und dein kleines Beispiel hilft mir leider auch nicht ...

Danke trotzdem


----------



## Morl99 (23. Sep 2010)

Was SlaterB vermutlich sagen wollte ist, dass es viele Lösungen für dein Problem gibt. Die beste Lösung auszuwählen ist nicht immer ganz einfach und hängt insbesondere von der schon vorhandenen Struktur, aber auch von der Größe des Projekts und vom Einsatzzweck ab.

Wenn du irgendeine Klasse hast (und die hast du bestimmt) die deine GUI initialisiert und quasi "über allem" steht, dann kann diese auch eine Referenz auf dein AnzeigeJPanel halten und bei bedarf weitergeben. Was in der GUI-Entwicklung häufig gemacht wird/wurde, ist das verwenden von Singeltons. D.h., dass es nur eine Instanz von einer bestimmten Klasse gibt (was bei einem Fenster ja Sinn macht). Wenn dies gegeben ist, kann diese Klasse eine statische Variable vom Typ der Klasse mit genau dieser Instanz halten und bei Bedarf z.B. über eine statische Methode 
	
	
	
	





```
getInstance()
```
 weitergeben.

Ob das sauber oder nicht ist, bleibt dann noch zu diskutieren. Funktionieren wird es aber in jedem Fall


----------



## Cru (23. Sep 2010)

Singleton...Design Patterns...da war ja was

Ich bin voller Zuversicht und werde es gleich mal versuchen ... danke schonmal


----------



## Cru (23. Sep 2010)

Puh...

Ich habe nun folgendes getan:

Aus meinem AnzeigeJPanel ein Singleton gemacht, indem ich folgendes ergänzt habe...


```
private static AnzeigeJPanel instance = new AnzeigeJPanel();
```

und


```
public static AnzeigeJPanel getInstance() {
		return instance;
	}
```

der Aufruf sieht nun so aus...


```
AnzeigeJPanel.getInstance().setBooleanTestCanvas(true);
```

Das kann nicht richtig sein, oder? Funktionieren tuts auf jeden Fall nicht ...

Langsam bin ich mit meinem Latein am Ende ???:L


----------



## Morl99 (23. Sep 2010)

Ich bin eher ein Freund der lazy initialization, in deinem Fall also

```
private static AnzeigeJPanel instance = null;
...
public static AnzeigeJPanel getInstance() {
  if (instance == null){
    instance= new AnzeigeJPanel();
  }
  return instance;
}
```

Das aber nur am Rande.

Was an deinem Aufruf falsch sein könnte leuchtet mir nun nicht ein, sieht doch gut aus. Wenn dein Problem noch besteht (was war dein Problem genau? passiert einfach nichts?) muss die Ursache woanders zu finden sein.

edit: Ansonsten kannst du ja mal den Debugger bemühen und schauen was nach dem zweiten Durchlauf von setCanvas für Componenten in deinem Container vorhanden sind. Alternativ hilft auch ein System.out.println(..) über container.getComponents(); (eventuell aber mit for-schleife(n)). Ich würde tippen, dass dein remove irgendwie nicht funktioniert... Mir ist aber gerade unklar, wie die aktuelel Version deines Codes aussieht, vielleicht kannst du die einfach nochmal posten.


----------



## Cru (23. Sep 2010)

Das eigentliche Problem ist, dass in meinem AnzeigeJPanel Komponenten ausgetauscht werden sollen.

Mit dem Singleton (so wie ich es habe) geht er auch in die If-Bedingung richtig hinein, allerdings ändert sich überhaupt nichts an der GUI...

Liegt das Problem jetzt daran wie ich den boolean-Wert übergebe oder doch in der Verwendung von remove?

Edit:

Nochmal der Code als Singleton...


```
public class AnzeigeJPanel extends JPanel {
	private static AnzeigeJPanel instance = new AnzeigeJPanel();
	private JScrollPane scrollPane;
	private AnzeigeTestCanvas anzeigeTestCanvas;
	private AnzeigeCanvas anzeigeCanvas;
	private boolean booleanTestCanvas;

	public AnzeigeJPanel() {
		setLayout(new BorderLayout());

		setzeCanvas(booleanTestCanvas);
	}

	private void setzeCanvas(boolean booleanTestCanvas) {
		this.booleanTestCanvas = booleanTestCanvas;

		Dimension dimensionBildschirm = Toolkit.getDefaultToolkit()
				.getScreenSize();
		System.out.println(getParent());
		if (booleanTestCanvas) {
			if (scrollPane != null) {
				System.out.println("AnzeigeCanvas wird gelöscht...");
				remove(scrollPane);
			}

			anzeigeTestCanvas = new AnzeigeTestCanvas();
			anzeigeTestCanvas.setPreferredSize(new Dimension(
					(dimensionBildschirm.width - 400),
					(dimensionBildschirm.height - 200)));
			add(anzeigeTestCanvas, BorderLayout.CENTER);

		} else {

			if (anzeigeTestCanvas != null) {
				System.out.println("AnzeigeTestCanvas wird gelöscht...");
				remove(anzeigeTestCanvas);
			}

			scrollPane = new JScrollPane();
			anzeigeCanvas = new AnzeigeCanvas();
			System.out.println(anzeigeCanvas);
			scrollPane.getViewport().setView(anzeigeCanvas);
			scrollPane.setPreferredSize(new Dimension(
					(dimensionBildschirm.width - 400),
					(dimensionBildschirm.height - 200)));
			scrollPane.setMaximumSize(new Dimension(1000, 1000));
			add(scrollPane, BorderLayout.CENTER);

		}
		validate();
		repaint();
	}

	public static AnzeigeJPanel getInstance() {
		return instance;
	}

	public void setBooleanTestCanvas(boolean booleanTestCanvas) {
		this.booleanTestCanvas = booleanTestCanvas;
		setzeCanvas(booleanTestCanvas);
	}
}
```

In der Konsole zu finden:

AnzeigeCanvas wird gelöscht...


Ich habe mir mal die Komponenten ausgeben lassen, nachdem der boolean-Wert gesetzt wurde:

gui.AnzeigeTestCanvas[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=0,maximumSize=,minimumSize=,preferredSize=java.awt.Dimension[width=880,height=600]]
javax.swing.JRootPane[,8,30,1280x738,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=]


Sieht eigentlich richtig aus?


----------



## Cru (23. Sep 2010)

Verdammt, verdammt...Problem gelöst 

Alles richtig, btt: Das Problem war nicht remove, sondern mein Zugriff auf die Klasse!

Mittels Umwandlung der Klasse in ein Singleton funktioniert alles einwandfrei.

Mein Fehler war noch(haha -.-) ich hab vergessen den Zugriff bei meiner Oberflaechen-Klasse auch zu ändern...da hatte ich ja die erste Instanz gebildet 


Danke an euch beide


----------



## Michael... (23. Sep 2010)

Cru hat gesagt.:


> Im Verlauf des Programmes wird die Klasse neu instantiiert und mit setBooleanTestCanvas(true); die Variable übergeben.


Ich vermute mal, dass die Instanz auf der die Methode aufgerufen wird, nicht die ist, die sichtbar ist.
Pack doch mal zum Testen noch ein JLabel auf das AnzeigePanel und setze beim Methodenaufruf einen Text rein , z.B. die Systemzeit.

Ansonsten finde ich remove und add während der Laufzeit eher unschön. Schau Dir mal das CardLayout an.


----------



## Michael... (23. Sep 2010)

OK, war ein bisschen zu spät, aber das ganze via Singleton zu lösen mag zwar funktionieren, aber... auch das finde ich eher unschon ;-)


----------



## Morl99 (23. Sep 2010)

Cru hat gesagt.:


> Ich habe mir mal die Komponenten ausgeben lassen, nachdem der boolean-Wert gesetzt wurde:
> 
> gui.AnzeigeTestCanvas[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=0,maximumSize=,minimumSize=,preferredSize=java.awt.Dimension[width=880,height=600]]
> javax.swing.JRootPane[,8,30,1280x738,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=]
> ...



Sind das nun die Komponenten in dem JPanel? Oder in dem Container dadrüber? Nun bin ich selber etwas verwirrt... Eigentlich sollte dein JPanel ja doch kein JRootPane haben... oder ???:L

Ach und mach mal ein 
	
	
	
	





```
scrollPane = null;
```
 unter 
	
	
	
	





```
remove
```
, das wird aber dein Problem nicht lösen, aber du planst ja eh keine Wiederverwendung, da können wir die Resourcen auch zur Freigabe bereitstellen.


----------



## Morl99 (23. Sep 2010)

Michael... hat gesagt.:


> OK, war ein bisschen zu spät, aber das ganze via Singleton zu lösen mag zwar funktionieren, aber... auch das finde ich eher unschon ;-)



Aus rein philosophischen Gründen? Oder spricht in diesem Fall wirklich etwas dagegen? Solange es wirklich nur eine Instanz von diesem Panel gibt kann man das doch problemlos so machen? Ich lasse mich aber gerne eines besseren belehren wenn du das begründen kannst 

PS: CardLayout ist hier allerdings in der Tat eine gute Idee!


----------



## Michael... (23. Sep 2010)

Mag zwar philosophisch sein ;-) aber ein Singleton verwende ich, wenn nur ein Objekt einer Klasse existieren _darf_. Und im Fall von GUI Komponten könnte es ja vorkommen, dass man eine weitere Instanz dieser Klasse an anderer Stelle einbauen will.
Und außerdem lernt man ja auch was dabei, wenn man versucht Probleme nicht mittels Krücken sondern richtig zu lösen ;-)


----------



## Morl99 (23. Sep 2010)

Michael... hat gesagt.:


> Mag zwar philosophisch sein ;-) aber ein Singleton verwende ich, wenn nur ein Objekt einer Klasse existieren _darf_. Und im Fall von GUI Komponten könnte es ja vorkommen, dass man eine weitere Instanz dieser Klasse an anderer Stelle einbauen will.


Ich kann aber auch festsetzen, dass es dieses Panel nur einmal geben darf!
(edit) Mh aber du hast schon recht... eigentlich dachte ich auch eher daran, aus dem Frame ein Singelton zu machen, denn von einem Frame macht es meistens keinen Sinn ein zweites zu erstellen (also zB dein MainFrame...)


> Und außerdem lernt man ja auch was dabei, wenn man versucht Probleme nicht mittels Krücken sondern richtig zu lösen ;-)


Kein Widerspruch ;-)


----------



## Cru (23. Sep 2010)

Also ich bin weitesgehend mit der Lösung zu frieden.

Auf dem JPanel befinden sich noch einige andere Komponenten(die hab ich hier bloß weggelassen). Und das JScrollPane soll zu einem späteren Zeitpunkt wieder verwendet werden(wenn der boolean-Wert wieder auf false gesetzt wird).

Mit dem CardLayout kann ich ja nochmal schaun...

Edit: Wie soll das aussehen wenn das Frame ein Singleton ist?

Achja, und frisst das CardLayout nicht mehr Ressourcen, wenn mehrere Komponenten zur gleichen Zeit da sind?


----------



## Morl99 (23. Sep 2010)

Cru hat gesagt.:


> Also ich bin weitesgehend mit der Lösung zu frieden.
> 
> Auf dem JPanel befinden sich noch einige andere Komponenten(die hab ich hier bloß weggelassen). Und das JScrollPane soll zu einem späteren Zeitpunkt wieder verwendet werden(wenn der boolean-Wert wieder auf false gesetzt wird).



In dem von dir gezeigten Code hast du dazu aber keine Chance... dann brauchst du auch hier eine Art lazyInitialization. Denn du erzeugst jedes mal eine neue ScrollPane bzw. ein neues Panel. Also ebenfalls ein 
	
	
	
	





```
if (bla = null) bla = new Bla();
```
 drum rum.



> Edit: Wie soll das aussehen wenn das Frame ein Singleton ist?


Naja dann ist das Frame ein Singleton und du machst 
	
	
	
	





```
Frame.getInstance().getAnzeigePanel().setBooleanTestCanvas(bool);
```



> Achja, und frisst das CardLayout nicht mehr Ressourcen, wenn mehrere Komponenten zur gleichen Zeit da sind?


Teuer ist hier vorallem das erstellen der Objekte und das revalidieren deines Panels. Das bloße merken der Komponenten ist nicht teuer. Und gezeichnet werden ja auch nur die momentan sichtbaren Komponenten. Also: Nein.

PS: Ein Cardlayout ist übrigens sehr einfach umzusetzen und ein nützliches Tool welches man auch in der Zukunft gebrauchen kann.


----------



## Cru (24. Sep 2010)

Ich hab das ganze jetzt mit einem CardLayout implementiert. Funktioniert auch ohne Probleme..^^

Danke nochmals


----------

