# JPanels verschachteln



## markai (13. Jan 2012)

Hallo!
Meine Freunde und ich arbeiten dzt. an einem kleinen Projekt bei dem jeder seinen eigenen Aufgabenbereich hat. Da die GUI (welche aus einem JFrame mit einem TabbedPane besteht) blöderweise als letztes gebaut wurde, hat nun jeder von uns sie seine App als eigenen JFrame gebaut.
Nun wollen wir das ganze aber zusammenfügen, aber das TappedPane verlangt ja JPanels.

Meine Frage:
Wie kann ich mehrere JPanels als Subpanels in ein JPanel einbauen?

Habe schon versucht die Panels aus der Pallette (arbeite mit NetBeans) hereinzuziehen und dann mit CustomizedCode mein selbst erstelltes Panel zu instanzieren. Das Ergebnis ist aber ein leeres JPanel.

Unser gesamtes Team hat das selbe Problem. Es ist zum verzweifeln ;(


----------



## hdi (13. Jan 2012)

Du arbeitest also mit einem GUI Builder Tool? Dann war das schon der erste Fehler. Grundsätzlich verstehe ich aber nicht ganz was dein Problem ist. Schachteln von Komponenten:


```
JPanel container = new JPanel();
container.setLayout(new GridLayout(2,2));
container.add(panel1);
container.add(panel2);
container.add(panel3);
container.add(panel4);
```


----------



## jgh (13. Jan 2012)

in dem du dem JPanel einen entsprechenden LayoutManager setzt.

hiermal ein kl. Bspw. von 3 JPanels die einem mainPanel geaddet werden und dann dem JFrame...


```
import java.awt.Color;
import java.awt.GridLayout;

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

public class MainFrame extends JFrame {

	JPanel p1, p2, p3;
	JPanel mainPanel;

	public MainFrame() {
		p1 = new JPanel();
		p1.setBackground(Color.black);
		p2 = new JPanel();
		p2.setBackground(Color.red);
		p3 = new JPanel();
		p3.setBackground(Color.yellow);

		mainPanel = new JPanel();
		mainPanel.setLayout(new GridLayout(3, 1));

		setSize(500, 500);
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainPanel.add(p1);
		mainPanel.add(p2);
		mainPanel.add(p3);

		add(mainPanel);
		setVisible(true);
	}

	public static void main(String[] args) {
		new MainFrame();
	}
}
```


----------



## markai (13. Jan 2012)

@hdi Swing macht das (u.a.) eh genau das, oder?



```
private void initComponents() {

        jPanel1 = new FolderHierarchy();
        jPanel2 = new ImagePanel();

        setLayout(new java.awt.BorderLayout());

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 1150, Short.MAX_VALUE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 196, Short.MAX_VALUE)
        );

        add(jPanel1, java.awt.BorderLayout.CENTER);

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 1150, Short.MAX_VALUE)
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 107, Short.MAX_VALUE)
        );

        add(jPanel2, java.awt.BorderLayout.PAGE_START);
    }// </editor-fold>                        
    // Variables declaration - do not modify                     
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    // End of variables declaration
```


----------



## jgh (13. Jan 2012)

markai hat gesagt.:


> Swing macht das (u.a.) eh genau das, oder?



Sorry, den Satz verstehe ich nicht...

Aber da du ja den GUI-Builder von Netbeans zu nutzen scheinst, kann und will ich dir dabei auch nicht helfen...ansonsten habe ich dir eine einfache Möglichkeit gezeigt, wie du bspw. 3 JPanels in einem JPanel anzeigen lassen kannst. Willst du allerdings weiterhin das GroupLayout nutzen, musst du auf andere Hilfe hoffen.


----------



## hdi (13. Jan 2012)

Nein. Du verwechselst Swing mit deinem GUI Tool. Das GUI Tool generiert dir diesen Code. Normalerweise schreibt man das alles selber. Und GroupLayout ist nur einer LayoutManager die es gibt, und kein besonders schöner.

edit: Wenn du mir nen Screenshot zeigst wie's aussehen soll kann ich dir entsprechenden Code im GridBagLayout schreiben, dann siehst du wie man das normalerweise selber schreibt. GUI tools erzeugen ultraschrottigen Code, und sind gut zum rumspielen um das Design für die GUI zu finden, haben aber imho nichts im Produktiv-Code verloren.


----------



## markai (13. Jan 2012)

@ jgh: sorry meine antwort war an hdi gerichtet. deine antwort kam schneller als meine. Ich bestehe NICHT auf dieses GUI Tool. Werde mich sogleich an die arbeit machen und deinen Code ausprobieren. Danke für die Hilfe!

@ hdi: Ok, dachte mir man kann das mit dem Tool ganz einfach machen. Aber wenn du meinst dass der code schrott ist wär ich doch eher für eigenen Code.

Wär toll wenn du mir dabei helfen könntest...
(Das JPanel ganz unten ist in einem ScrollPanel, da werden die Bilder hineingeladen wenn sich welche im Ordner befinden)


----------



## hdi (13. Jan 2012)

Oh, naja ich dachte die GUI ist etwas simpler  Ich hoffe du verstehst dass ich jetzt nicht den gesamten Layout-Code schreiben will. Aber grundsätzlich funktioniert das so, dass du auf deinem Container erst den Layout-Manager festlegst (setLayout) und dann Komponenten addest (add/evtl mit Constraints). Einen Überblick über die möglichen Layout-Manager findest du hier:

A Visual Guide to Layout Managers (The Java™ Tutorials > Creating a GUI With JFC/Swing > Laying Out Components Within a Container)

zB deine Buttons im rechten Bereich kannst du schön mit nem GridLayout untereinander ordnen:


```
JPanel buttons = new JPanel();
buttons.setLayout(new GridLayout(0,1));
buttons.add(yourButton1);
buttons.add(yourButton2);
// usw...
```

Das gesamte buttons Panel wiederum kannst du dann einer übergeordneten Komponente adden. zB wenn du diese Buttons jetzt kombinieren willst mit deinem Hauptbereich, wo die Ordner angezeigt werden:


```
JPanel overallContent = new JPanel();
overallContent.setLayout(new BorderLayout());
overallContent.add(BorderLayout.CENTER, yourDirectoryPanel);
overallContent.add(BorderLayout.EAST, buttons);
```

So kann man das halt schachteln. Der Grund-Container ist bei nem Frame das Content Pane, also wenn zu dem overallContent im obigen Code nix mehr dazu kommen würde, dann könntest du gleich das Content Pane dafür benutzen:


```
yourJFrame.getContentPane().setLayout(new BorderLayout());
yourJFrame.getContentPane().add(BorderLayout.CENTER, yourDirectoryPanel);
yourJFrame.getContentPane().add(BorderLayout.EAST, buttons);
```

Wobei das getContentPane() gar nicht nötig ist. Du kannst direkt yourJFrame.add() sagen, denn das leitet intern ans Content Pane weiter.

Es gibt nun auch Layouts wo du nicht einzelne Teilbereiche schachtelst, sondern gleich alles auf einer Ebene zusammenfügst. Dafür bietet sich zB das GridBagLayout an. D.h. du sagst dann direkt:

yourJFrame.getContentPane().setLayout(new GridBagLayout());
yourJFrame.getContentPane().add(_<component>, <gridBagConstraints>_);

Beim GBL musst du beim adden sog. GridBagConstraints angeben, über die du für die jeweilige Komponente spezifierist wo genau sie liegen soll, wie hoch/breit sie sein soll, inwiefern sie sich beim Resize anpassen soll usw.

Auf jeden Fall solltest du dich mal damit beschäftigen. Deinen GUI Builder solltest du nicht mehr verwenden (nur noch zum Rumspielen mit dem Design, wenn du dir noch überlegen willst wie's am Ende aussehen soll).

Wenn du konkrete Hilfe mit einem Layout brauchst, dann sag Bescheid.


----------



## markai (13. Jan 2012)

Omg! Tausend dank für deine ausführliche Antwort!! (Im echten Leben hättest du dir dafür ein Sixpack verdient  ) Hatte heute leider keine Zeit mehr, werd mich aber morgen dahinterklemmen und dir dann bescheid geben. Ich vermute mal du hast mir damit eine Menge Zeit und Nerven gespart (Projekt muss am Do fertig sein). Vielen Dank :toll:


----------



## markai (14. Jan 2012)

Habe heute schon versucht mein Hauptpanel(=Testpanel) mit Hilfe des GridBagLayouts zu layouten. Dazu habe ich zuerst, wie ihr mir geraten habt, auf meinen GUI-Builder verzichtet und das ganze (ButtonPanel, FolderHierarchy und ImagePanel) als Class welche JPanel extended neu erstellt. Bin mir aber nicht sicher welches Layout ich für diese Panels verwenden soll, da dafür ja eigentlich kein Layout notwendig wäre (alle Ordner und Bilder werden über paintComponent() gezeichnet), also habe ich in der initComponent jeweils nur die größe gesetzt.


```
setPreferredSize(new java.awt.Dimension(1300, 500));
```

in meinem Hauptpanel (=Testpanel) welches später in der ferigen GUI dann instanziert werden soll habe ich folgendes gemacht:


```
public class TestPanel extends JPanel {

    JPanel folderHierarchyPanel, buttonsPanel, imagePanel;
  
    public TestPanel() {
        folderHierarchyPanel = new FolderHierarchy2();
        imagePanel = new ImagePanel2();
        buttonsPanel = new ButtonsPanel();
        GridBagLayout gbl = new GridBagLayout();
        this.setLayout(gbl);
        GridBagConstraints gbc = new GridBagConstraints();
        
        gbc.fill = GridBagConstraints.HORIZONTAL;
        
        gbc.insets = new Insets(1, 1, 1, 1);
        
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridheight = 1;
        gbl.setConstraints(folderHierarchyPanel, gbc);
        add(folderHierarchyPanel);

        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.gridheight = 2;
        add(buttonsPanel);

        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.gridheight = 1;
        ScrollPane sp = new ScrollPane();
        sp.add(imagePanel);
        gbl.setConstraints(sp, gbc);
        add(sp);

        this.setVisible(true);
    }
```

In meinem JFrame mache ich folgendes:

```
public class App2 extends JFrame {

    JPanel testPanel = new TestPanel();

    public App2() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.getContentPane().add(testPanel);
    }

    public static void main(String[] args) {
        App2 demo = new App2();
        demo.setVisible(true);
    }
}
```

Herauskommt dabei aber irgendwas. (siehe anhang)

Zum besseren Verständnis, wie das ganze aufgebaut ist habe ich eine Skizze angefügt.

Bitte um Rat! Was mache ich falsch?


----------



## hdi (14. Jan 2012)

Ich würd das ganze so machen:

ButtonPanel hat GridLayout(0,1). Dort dann einfach deine Buttons per add() von oben nach unten hinzufügen und fertig.

ImagePanel, da kannste wohl ein FlowLayout nehmen. Ich hoffe du malst die einzelnen Bilder nicht direkt per paintComponent da rein. Das ist Quatsch, mach dir ne eigenen Komponenten-Klasse "ImageComponent extends JComponent". Dort malst du in der paintComponent das Bild, aber für jedes Bild hast du n eigenes ImageComponent, und die Dinger addest du ins ImagePanel einfach über add() ins FlowLayout.

Dass in deiner FolderHierarchy die Ordner per paint gezeichnet werden ist der selbe Quatsch. Abgesehen davon dass das ganze dann wohl undynamisch ist macht es das doch auch viel komplizierter zu ermitteln welchen Ordner man angeklickt hat usw. Mach dir auch hier ne eigene Klasse FolderComponent extends JComponent, und dann verpass deine Hierarchy ein passendes Layout.

Dein Content Pane könnte jetzt im einfachsten Fall ein BorderLayout haben. Allerdings scheint es ja dass das Button Panel die gesamte Höhe einnehmen soll. Beim BorderLayout wäre das nicht der Fall, da würde sich die untere Komponente (Image Panel) über die gesamte Breite legen.

GridBagLayout ist da also durchaus ne gute Wahl. Ich hab mir jetzt deinen Code nicht genau angesehen, aber so müsste das funktionieren:


```
frame.getContentPane().setLayout(GridBagLayout());
GridBagConstraints c = new GridBagConstraints();

c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 1;
frame.getContentPane().add(folderHierarchy, c);

c.gridx = 1;
c.gridy = 0;
c.fill = GridBagConstraints.VERTICAL;
c.weightx = 0;
c.weighty = 1;
c.gridheight = 2;
frame.getContentPane().add(buttonsPanel, c);

c.gridx = 0;
c.gridy = 1;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.weighty = 0;
c.gridheight = 1;
frame.getContentPane().add(imagePanel, c);

frame.pack();
```


----------



## markai (14. Jan 2012)

Danke erstmal. Wenn du sagst "FolderHierarchy die Ordner per paint gezeichnet werden ist Quatsch" meinst du dann damit dass das keine schöne Lösung ist, oder dass das ganze so nicht funktionieren wird und ich alles neu machen soll (denn ich habs per paintComponent gemacht, und das war eine Menge arbeit :shock


----------



## bERt0r (14. Jan 2012)

Das war sicher eine Menge Arbeit, die du dir ersparen hättest können, weils viel einfacher geht mit einem Layoutmanager. Ob du die Zeit investieren willst zu lernen, wie mans einfacher und eleganter löst bleibt dir überlassen.
Was ich hier im übrigen Anmerken wollte: jeder JFrame speichert seine ganzen Komponenten die er anzeigt in einem Container, das ist das ContentPane. Wenn du dir den von einem Frame mittel getContentPane() holst, kannst du eben diesen Container wieder in einem anderen Frame nach deinen Wünschen einbauen.
z.B

```
Frame1 f1=new Frame1();
Frame2 f2=new Frame2();
JFrame f3=new JFrame();

JPanel zweiInEins=new JPanel();
zweiInEins.add(f1.getContentPane());
zweiInEins.add(f2.getContengPane());
f3.setContentPane(zweiInEins);
```


----------



## hdi (14. Jan 2012)

> Wenn du sagst "FolderHierarchy die Ordner per paint gezeichnet werden ist Quatsch" meinst du dann damit dass das keine schöne Lösung ist, oder dass das ganze so nicht funktionieren wird und ich alles neu machen soll


Kommt drauf an was du noch vorhast.. Man findet immer _irgendwie _einen Weg. Aber man kann's sich halt schwer machen oder einfach. Der Weg, den du gewählt hast, ist der schwere. D.h. auch im Sinne von nicht schön, nicht elegant. Wenn's jetzt funktioniert, naja dann funktioniert's offenbar. Aber ob dir das in naher Zukunft bei der Implementierung eines neuen Features zum Verhängnis wird, kann man halt nicht sagen. Wie ist das denn zB beim Resize des Frames? Passt sich das Panel dann an, also verändert sich die Größe und/oder Position der Ordner? Das wär auch so ne Sache, die dir ein Layout Manager komplett abnehmen würde. Ich würd's umbauen. Das sollte auch nicht so lange dauern. Es kommt darauf an nach welchem Muster du die Ordner grad zeichnest. Man erkennt ja ne Art Baumstruktur. Die Frage ist ob du die Dinger vllt nicht gleich in nem JTree anzeigen willst - dann musst du nämlich gar nix mehr zeichnen.


----------



## markai (14. Jan 2012)

Ich will zwar schon lernen wie manns richtig macht, aber da das Projekt - wie gesagt - am Do abgegeben werden muss u ich mit meinen bescheidenen Programmierkenntnissen bereits ca. 40h in diese Ordnerstruktur investiert habe und mehrmals an meine Grenzen gestoßen bin will ich diese JETZT nicht unbedingt umbauen müssen. Bitte habt dafür verständnis und helft mir mit meinem Code. Ich weiß nicht was ich falsch mache... ;(


----------



## hdi (14. Jan 2012)

Ich hab dir doch bereits den korrekten Layout-Code gepostet.


----------



## markai (14. Jan 2012)

schon, aber ich würde gerne das main panel zusammenbauen (also alle components FolderHierarchy(), ButtonsPanel()...). und DANN dieses als ganzes im Fram instanzieren.

Wenn ich deinen code richtig verstanden habe baust du dein panel erst im frame zusammen.

```
frame.getContentPane().setLayout(GridBagLayout());
```


----------



## bERt0r (14. Jan 2012)

Bevor du mit dem Gridbaglayout herumhantierst, weisst du überhaupt wie man mit einem BorderLayout umgeht? Du solltest erstmal die paar einfache Layoutmanager lernen.


----------



## hdi (14. Jan 2012)

Dann mach dir ein neues JPanel p und ersetze in meinem Code jedes Vorkommen von frame.getContentPane() durch dieses p. Das p addest du dann ins frame per frame.getContentPane().add(p). Was dir das bringen soll verstehe ich allerdings nicht. Du hast dann einfach eine weitere Komponenten-Schicht auf dem Frame, die keine weitere Unterteilung vornimmt. Also unnötig.


----------



## markai (14. Jan 2012)

Dachte ich kriegs mit dem BorderLayout nicht so hin dass es aussieht wie ich will (siehe screenshot, letzter post) darum hab ichs gleich mit dem gridBagLayout versucht. Wär das besser geeignet?


----------



## hdi (14. Jan 2012)

Sorry, mach mal die Augen auf:


> Dein Content Pane könnte jetzt im einfachsten Fall ein BorderLayout haben. Allerdings scheint es ja dass das Button Panel die gesamte Höhe einnehmen soll. Beim BorderLayout wäre das nicht der Fall, da würde sich die untere Komponente (Image Panel) über die gesamte Breite legen.
> 
> GridBagLayout ist da also durchaus ne gute Wahl.


----------



## GUI-Programmer (14. Jan 2012)

Wenn dir GridBagLayout nicht gefällt, kann ich dir nur FormLayout von jgoodies empfehlen. Das ist sehr ähnlich aufgebaut aber *meiner Meinung nach* leichter zu verwenden. Musst du halt selbst wissen, was dir besser liegt.


----------



## bERt0r (14. Jan 2012)

Nicht so schnell erstmal. Der TO hat schon probleme damit ein vernünftiges BorderLayout anzuzeigen.
Mit Gridbag und Formlayout verwirrt ihr ihn nur mehr.
Versuchs doch einfach mal mit dem BorderLayout und schau was dabei rauskommt. Wenn du das richtig hinbekommst, fällts dir mit dem GridbagLayout garantiert leichter.


----------



## markai (15. Jan 2012)

Ich habs gestern dann doch noch mit dem GridBagLayout hinbekommen. Mein Fehler war glaub ich dass das JPanel zu groß war als dass es in meinen JFrame hineingepasst hätte. Hab meinen Frame dann größer gemacht und auf einmal war alles da. Hatte also im grunde eh (fast) alles richtig gemacht. Blöder Fehler eigentlich...

Wegen des JTrees: Das Programm soll dazu dienen älteren Menschen den Umgang mit einem Computer zu erleichtern. Meiner Erfahrung nach scheitert es oft daran, dass diese sich nicht vorstellen können wie eine Ordnerstruktur aufgebaut ist. Mit einem JTree hätten sie das gleiche Problem...

Danke an alle die mir geholfen haben :toll:


----------

