# JPanel in JScrollPane?



## WeeN (23. Mai 2008)

Hallo, ich habe folgendes Problem:

Ich möchte Elemente, hier eigene JPanels, auf einem "Parent"-JPanel anordnen. Dieses Parent-JPanel soll innerhalb einer JScrollPane angezeigt werden und demzufolge auch bei einem zu großen Parent-JPanel scrollbar sein. Nun weiss ich nicht, ob das so funktioniert. 

Ich habe im Forum gesucht, aber nichts wirklich passendes gefunden. Es gab scheinbar einen ähnlichen Threat, wo dann aber wegen angeblicher Trivialität  auf die Sun-Tutorials zu JScrollPane verwiesen wurde.

Mir ist nach Durchlesen des Tutorials trotzdem noch einiges unklar:

- im Bezug auf mein Problem: muss ich ein eigenes JPanel ableiten, das Scrollable implementiert, da das Standard-JPanel dies nicht implementiert?(Das Sun-Tutorial nimmt ein abgeleitetes JLabel, weshalb ich meine Vermutung als bestätigt sehe, aber lieber nochmal nachfrage)

- was sagt "preferredSize" konkret aus? Muss ich keine Größenangaben mehr treffen, sondern setze nur noch die Position und preferredSize und das Element nimmt meine gewünschten Maße an?

- wie steht die preferredSize im Zusammenhang mit dem anzeigenden Element(der JScrollPane) und dem Client(in meinem Fall das Parent-JPanel)? 
          a) Muss ich, wenn ich dem JScrollPane eine feste Größe angegeben habe, aber noch keine preferredSize, die 
            preferredSize auf die aktuell Größe des JScollPane setzen? 
          b) Muss die preferredSize des Clients(hier das JPanel) die Gesamtgröße des JPanels sein oder nur der sichtbare
            Ausschnitt ?

Zusätzlich zu dem Problem ist noch, das später die JPanels, die sich innerhalb des Parent-Panels befinden, durch Angabe einer Zahl entsprechend verändern sollen. Wird z.B. eine 5 angegeben, sollen 5 JPanels in dem Parent-JPanel erscheinen. Wird diese Anzahl erhöht oder verringert, ändert sich demzufolge auch die Größe des Parent-JPanels und der Scrollbereich der JScrollPane. 
Mögliche Vorgehensweise:
Ich packe die JPanels standardmäßig in ein Array.
Bei einer Änderung verändere das Array entsprechend(Array umkopieren in ein neues Passendes), bei kleinerer Anzahl mache ich ein remove(), bei größerer ein add(), setze ev. vorher das Parent-JPanel auf die neue Größe.
Muss ich dan nur den revalidate()-Aufruf machen, oder gibt es noch mehr zu beachten?

Ich hoffe, das meine Absicht erkennbar ist und ich meine Probleme klar dargelegt habe. Im Folgenden noch ein Beispiel-Code, der das ganze etwas verdeutlichen soll. 




```
/**
 * @author WeeN
 */

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.LineBorder;

public class JScrollPaneEX extends JFrame{

    public JScrollPaneEX() {
        
        //*****************Grundlegende Fenstereigenschaften*******************
        super("Testframe");
        this.addWindowListener( new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event){
                    event.getWindow().setVisible(false);
                    event.getWindow().dispose();
                    System.exit(0);
            }
        });
        this.setSize(500,400);
        this.setResizable(false);
        this.setLocation(200, 200);
        
        //**********************Beginn Zusätzliches Zeug***********************
        //Hauptcontainer
        Container cp = getContentPane();
        cp.setLayout(null);
        
        //**********************Inneres Feld ohne Rand*************************
        Dimension d = getSize();
        d.height = d.height - getInsets().top - getInsets().bottom;
        d.width = d.width - getInsets().left - getInsets().right;
        
        //***********************Eigene Panels erzeugen************************
        PersonalPanel[] pp = new PersonalPanel[3];
  
        for(int i =0 ; i < pp.length; i++){
            pp[i] = new PersonalPanel();
        }    
        
        //********************Parent-Panel*************************************
        JPanel jp = new JPanel(null);
        
        //****************Eigene Panels zum Parent-Panel hinzufügen************
        for(int i = 0; i < pp.length; i++){
            jp.add(pp[i]);
            pp[i].setLocation(10, 10 + (i * (pp[0].getHeight() + 10)) );
        }
        
        //****************************JScrollPane******************************
        JScrollPane scrollPane = new JScrollPane(jp,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setSize(440,(int) (d.height / 4) * 2);
        cp.add(scrollPane);
        scrollPane.setLocation((int)(d.width - 440 ) / 2, (int)d.height / 4 );
        
         scrollPane.getViewport().add(jp);
         cp.invalidate();
         cp.validate();
         

        
    }

    public static void main(String[] args){
        JScrollPaneEX frame = new JScrollPaneEX();
        frame.setVisible(true);
    }
    
    
    class PersonalPanel extends JPanel{
        
        JLabel jLbl_Bezeichner;
        JTextField jTxtFld_Bezeichner;
        JLabel jLbl_Punkte;
        JSpinner jSpin_Punkte;
        JLabel jLbl_Auswahl;
        JComboBox jCmbBx_Auswahl;
        
        public PersonalPanel(){
            //Initialisierung Grund-Panel
            super(null);
            
            JPanel me = this;
            
            me.setSize(420, 80);
            //Zur Kenntlichmachung des Panels mit Border versehen
            me.setBorder(new LineBorder(new Color(34,12,45), 2));
            
            //Bezeichnungslabel Initialisierung
            jLbl_Bezeichner = new JLabel("Position");
            jLbl_Bezeichner.setSize(100, 25);
            me.add(jLbl_Bezeichner);
            jLbl_Bezeichner.setLocation(10,10);
            
            //Bezeichnungstextfeld Initialisierung
            jTxtFld_Bezeichner = new JTextField();
            jTxtFld_Bezeichner.setSize(170, 25);
            me.add(jTxtFld_Bezeichner);
            jTxtFld_Bezeichner.setLocation(115,10);
            
            //Punktelabel Initialisierung
            jLbl_Punkte = new JLabel("Punkte");
            jLbl_Punkte.setSize(80, 25);
            me.add(jLbl_Punkte);
            jLbl_Punkte.setLocation(290,10);
            
             //Punkte-Spinner Initialisierung
            SpinnerNumberModel snm = new SpinnerNumberModel(1, 0, 20, 1);
            jSpin_Punkte = new JSpinner(snm);
            jSpin_Punkte.setSize(35, 25);
            me.add(jSpin_Punkte);
            jSpin_Punkte.setLocation(375,10);
            
            //Dienstrang-Label Initialisierung
            jLbl_Auswahl = new JLabel("Gruppe");
            jLbl_Auswahl.setSize(100, 25);
            me.add(jLbl_Auswahl);
            jLbl_Auswahl.setLocation(10,45);
            
            //Dienstrang-ComboBox Initialisierung
            jCmbBx_Auswahl = new JComboBox();
            jCmbBx_Auswahl.setSize(210, 25);
            me.add(jCmbBx_Auswahl);
            jCmbBx_Auswahl.setLocation(115,45);
        }
        
        
    }
    
}
```
[/code]


----------



## WeeN (26. Mai 2008)

Sobald auch nur irgendeine Komponente ein null-Layout verwendet, kann scheinbar nicht die bevorzugte Größe ermittelt werden, weswegen es bei der Anwendung des null-Layouts nur den sichtbaren Bereich, hier die Größe des Scrollbereichs, anzeigt, aber keien Scrollbalken, selbst wenn der anzuzeigende Raum größer ist. Die Erfahrung musste ich machen, nachdem ich versuchte, das JPanel zu boxen, sprich, es einfach in ein anderes Panel, diesmal mit Layout, zu packen. Der 2. Versuch, die Verwendung des AbsoluteLayouts von Sun, scheiterte daran, das zwar meine Komponenten brav an ihrer Position waren, aber das Panel direkt mit den äußeren Kanten der Komponenten aprupt endete. Abhilfe dagegen ließ sich dadurch schaffen, die Panels wiederum absolut im Parent-Panel zu positionieren. Mein letztendlicher Aufbau sieht so aus, das die Panel ihre Maße mithilfe des GridBagLayouts erhalten und dann absolut positioniert werden.

Das nachfolgende Beispiel zeigt noch einmal das Problem. Gleichzeitig ist hier auch schon das Problem mit dem dynamischen Hinzufügen und Entfernen gelöst. Da bestand noch das Problem, das, wenn man die JPanels 2 & 3 nicht versteckt, diese noch angezeigt werden, obwohl sie eigentlich gelöscht sein sollten. Meine Vermutung ist, das es ein Problem mit dem neuzeichnen gibt, besser gesagt mit dem sichtbaren Bereich des JScrollPane. Entfernt man die Befehl JPanel.setVisible(false), kann man das Problem beobachten.

Threat kann hiermit geschlossen werden


```
/**
 * @author WeeN
 */

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import org.netbeans.lib.awtextra.*;

public class DisplayFrame extends JFrame{

    
    
    public static final int ANZAHL= 1;
    static final int OFFSET = 10;
    static final int HOEHE = 80;
    
    private JScrollPane scroll = new JScrollPane();
    private JPanel panel = new JPanel();
    private JSpinner spin = new JSpinner(new SpinnerNumberModel(1, 1, 30, 1));
    private JPanel contentHolder = new JPanel(new AbsoluteLayout());
    PersonalPanelGridbag[] pp = null;
    
    public DisplayFrame() {
        
        //*****************Grundlegende Fenstereigenschaften*******************
        super("Testframe");
        this.addWindowListener( new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event){
                    event.getWindow().setVisible(false);
                    event.getWindow().dispose();
                    System.exit(0);
            }
        });
        
        this.setLayout(new BorderLayout());
       
        //*******************Beginn*******************************
        pp = new PersonalPanelGridbag[ANZAHL];
        scroll.setSize(420, 300);
        add(scroll,"Center");
        spin.setSize(35,25);
        panel.add(spin);
        panel.setSize(420,50);
        add(panel,"South");
        
        Dimension d =  new Dimension(420, 80);
        for(int i= 0; i < pp.length;i++){
            Point p = new Point(0 , OFFSET + i* (HOEHE+ OFFSET));
            AbsoluteConstraints ac = new AbsoluteConstraints(p, d);
            pp[i] = new PersonalPanelGridbag();
            contentHolder.add(pp[i],ac);
        }
        spin.addChangeListener(new ValueListener());
        spin.setValue(new Integer(pp.length));
        scroll.setViewportView(contentHolder);
     
    }

    public static void main(String[] args){
        DisplayFrame frame = new DisplayFrame();
        frame.setSize(444,400);
        frame.setResizable(false);
        frame.setLocation(200, 200);
        frame.setVisible(true);
    }
    
    
    class ValueListener implements ChangeListener{

        public void stateChanged(ChangeEvent e) {
            Integer temp =(Integer) spin.getValue();
            
            int neueGroesse = temp;
            
            if(pp.length == neueGroesse){
                return;
            }else if(pp.length < neueGroesse){
                //*************Beginn Anzahl größer********************
                //temporäres Array
                PersonalPanelGridbag[] tmpPPG = new PersonalPanelGridbag[neueGroesse];
                //Array-Kopie vom Original in temporäres Array
                for(int it = 0; it < pp.length;it++){
                    tmpPPG[it] = pp[it];
                }
                //temporäres auf Original zuweisen               
                pp = tmpPPG;
                //Maße
                Dimension d = new Dimension(420, 80);
                AbsoluteConstraints ac =  new AbsoluteConstraints(0, 0);
                //Array durchlaufen, null-Elemente füllen und hinzufügen
                for(int i= 0;i < pp.length; i++ ){
                    if(pp[i] == null){
                        pp[i] = new PersonalPanelGridbag();
                        Point p = new Point(0 , OFFSET + i* (HOEHE+ OFFSET));
                        ac = new AbsoluteConstraints(p, d);
                        contentHolder.add(pp[i],ac);
                    }
                }
            }else{
                //****************Beginn Anzahl kleiner***************
                System.out.println(neueGroesse);
                PersonalPanelGridbag[] tmpPPG = new PersonalPanelGridbag[neueGroesse];
                //Array-Kopie vom Original in temporäres Array
                for(int it = 0; it < neueGroesse;it++){
                        tmpPPG[it] = pp[it];
                }
                for(int i = neueGroesse; i < pp.length ;i++){
                    pp[i].setVisible(false);
                    contentHolder.remove(pp[i]);
                    pp[i] = null;
                }
                //temporäres auf Original zuweisen
                pp = tmpPPG;
            }//Ende else
            
        Container cont = (Container) scroll;
        cont.invalidate();
        cont.validate();
        }
        
    }
    
    class PersonalPanelGridbag extends JPanel{
        
        JLabel jLbl_Posten;
        JTextField jTxtFld_Posten;
        JLabel jLbl_Punkte;
        JSpinner jSpin_Punkte;
        JLabel jLbl_Gruppe;
        JComboBox jCmbBx_Gruppe;
        
                 
        public PersonalPanelGridbag(){
            //Initialisierung Grund-Panel
            super();
            GridBagLayout gbl = new GridBagLayout();
            setLayout(gbl);
            
            
            Insets inset= new Insets(10, 10, 10, 0);
            setSize(420, 80);
            
            //Bezeichnungslabel Initialisierung
            jLbl_Posten = new JLabel("Posten");
            jLbl_Posten.setSize(100, 25);
            GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.ABOVE_BASELINE, GridBagConstraints.NONE, inset, 0,0);
            gbl.setConstraints(jLbl_Posten, gbc);
            add(jLbl_Posten);
            //Bezeichnungstextfeld Initialisierung
            jTxtFld_Posten = new JTextField();
            jTxtFld_Posten.setSize(170, 25);
            inset= new Insets(10, 5, 5, 0);
            gbc = new GridBagConstraints(1, 0, 1, 1, 2.0, 0.0, GridBagConstraints.ABOVE_BASELINE, GridBagConstraints.HORIZONTAL, inset, 0,0);
            gbl.setConstraints(jTxtFld_Posten, gbc);
            add(jTxtFld_Posten);
            //Punktelabel Initialisierung
            jLbl_Punkte = new JLabel("Punkte");
            jLbl_Punkte.setSize(80, 25);
            gbc = new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.ABOVE_BASELINE, GridBagConstraints.NONE, inset, 0,0);
            gbl.setConstraints(jLbl_Punkte, gbc);
            add(jLbl_Punkte);
             //Punkte-Spinner Initialisierung
            SpinnerNumberModel snm = new SpinnerNumberModel(1, 0, 20, 1);
            jSpin_Punkte = new JSpinner(snm);
            jSpin_Punkte.setSize(35, 25);
            inset= new Insets(10, 5, 5, 10);
            gbc = new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.ABOVE_BASELINE, GridBagConstraints.NONE, inset, 0,0);
            gbl.setConstraints(jSpin_Punkte, gbc);
            add(jSpin_Punkte);
            //Dienstrang-Label Initialisierung
            jLbl_Gruppe = new JLabel("Gruppe");
            jLbl_Gruppe.setSize(100, 25);
            inset= new Insets(5, 10, 10, 0);
            gbc = new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.ABOVE_BASELINE, GridBagConstraints.NONE, inset, 0,0);
            gbl.setConstraints(jLbl_Gruppe, gbc);
            add(jLbl_Gruppe);
            //Dienstrang-ComboBox Initialisierung
            jCmbBx_Gruppe = new JComboBox();
            jCmbBx_Gruppe.setSize(210, 25);
            inset= new Insets(5, 5, 10, 0);
            gbc = new GridBagConstraints(1, 1, 1, 1, 2.0, 0.0, GridBagConstraints.ABOVE_BASELINE, GridBagConstraints.HORIZONTAL, inset, 0,0);
            gbl.setConstraints(jCmbBx_Gruppe, gbc);
            add(jCmbBx_Gruppe);
        }
    }   
}
```


----------



## Marco13 (26. Mai 2008)

Ach ja, da wollte ich ja noch antworten ... aber die Fragen waren ziemlich viel, durcheinander und unstrukturiert (und ich geh' meistens nur auf "Neue Beiträge"). Wenn sich's inzwischen erledigt hat, ist's ja gut. Aber nochmal kurz zum snychronisieren:

_- im Bezug auf mein Problem: muss ich ein eigenes JPanel ableiten, das Scrollable implementiert, da das Standard-JPanel dies nicht implementiert?_
Nein. Scrollable muss man nur implementieren, wenn man das "Scroll-Verhalten" (Schrittgößen etc.) gezielt beeinflussen will.

_- was sagt "preferredSize" konkret aus? Muss ich keine Größenangaben mehr treffen, sondern setze nur noch die Position und preferredSize und das Element nimmt meine gewünschten Maße an?_

Die PreferredSize ist die Größe, die die Component gerne hätte. Standarmäßig (z.B. bei einem JPanel) wird die PreferredSize _berechnet_, auf Basis des aktuellen Layouts, und der PreferredSize der enthaltenen Components. Wenn man die PreferredSize explizit setzt (mit setPreferredSize) wird aber immer die gesetzte verwendet! Und wenn das JPanel ein null-Layout hat, kann natürlich nichts berechnet werden, d.h. dann muss man sie explizit setzen.

_- wie steht die preferredSize im Zusammenhang mit dem anzeigenden Element(der JScrollPane) und dem Client(in meinem Fall das Parent-JPanel)? 
_
Innerhalb einer ScrollPane wird die Component mit ihrer preferredSize dargestellt, FALLS diese größer ist, als der darstellbare Bereich. Wenn die PreferredSize kleiner ist, wird die Component auf die Größe des sichtbaren Bereiches aufgeblasen.

_a) Muss ich, wenn ich dem JScrollPane eine feste Größe angegeben habe, aber noch keine preferredSize, die 
            preferredSize auf die aktuell Größe des JScollPane setzen? _
Die Größe, die man mit setSize setzt, ist egal: Wie groß die ScrollPane ist, bestimmt der LayoutManager der Component, in der die ScollPane liegt. (Einige LayoutMaanger berücksichtigen dabei, falls möglich und sinnvoll, die preferredSize der ScollPane)

_ b) Muss die preferredSize des Clients(hier das JPanel) die Gesamtgröße des JPanels sein oder nur der sichtbare
            Ausschnitt ?_
Man braucht sie nicht explizit zu setzen, außer bei einem null-Layout. Und WENN man sie setzt, dann sollte sie die Gesamtgröße beschreiben.


----------

