~Java4Beginners~
~Java4Beginners~

GridBagLayout

Das GridBagLayout zählt zu den mächtigsten LayoutManagern in Java und ist etwas komplizierter zu verstehen. Mit dem GridBagLayout hab ich eine Vielzahl von Möglichkeiten, meine Bedienelemente zu platzieren. Ich habe weiterhin die Möglichkeit, meine Bedienelemente dynamisch zu gestalten, d. h. eine automatische Größenanpassung an eine veränderte Fenstergröße vorzunehmen. Auf Grund der Komplexität des LayoutManagers werde ich hier nun schrittweise vorgehen, um die einzelnen Bestandteile genauer zu betrachten, insbesondere Ihre Auswirkungen.

Konstruktor

Das GridBayLyout bietet uns lediglich den Standardkonstruktor an. Sämtliche Einstellungen des Designs müssen wir als mit Methoden vornehmen.

Das Layout, bzw. das Verwalten des Layouts erfolgt über die Klasse GridBagConstraints. Es empfiehlt sich, in einem GridBag-Layout ein Objekt dieser Klasse zu erzeugen, da es die Arbeit um einiges erleichtert.

Vorteil des GridBagLayouts



Auf dem ersten Blick lässt sich der Vorteil des GridBagLayouts so nicht erkennen. Der Vorteil des Layout-Managers zeigt sich, wenn wir die Fenstergröße ändern. Alle Bedienelemente werden relational zur neuen Fenstergröße angepasst, ohne dass sich am Grundlayout etwas ändernt.

Beispielprogramm

Als erstes hier einmal ein Programm in seiner Gesamtheit. Anschließend werde ich darüber einige Worte verlieren.

import java.awt.Component;
import java.awt.GridBagConstraints;
import static java.awt.GridBagConstraints.BOTH;
import static java.awt.GridBagConstraints.CENTER;
import static java.awt.GridBagConstraints.FIRST_LINE_START;
import static java.awt.GridBagConstraints.HORIZONTAL;
import static java.awt.GridBagConstraints.LINE_START;
import static java.awt.GridBagConstraints.NONE;
import static java.awt.GridBagConstraints.RELATIVE;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * Beispielprogramm für GridBagLayout
 * @author Markus Badzura
 */
public class LayoutGridBag extends JFrame
{   
    // Container
    JPanel panel_grid;
    // Bedienelemente 
    private JTextField txtName, txtAlter;
    private JTextArea txaKommentar;
    private JButton btOk;
    
    //GridBagConstraints Argumente
    private int gridx, gridy, gridwidth, gridheight, fill, anchor, ipadx, ipady;
    private double weightx, weighty;
    private Insets insets;
    
    //GridBadConstraints Insets für Bedienelemente
    private int oben, links, unten, rechts;
    private final Insets insetsOben = new Insets(oben = 5, links = 0, unten = 15, rechts = 0);
    private final Insets insetsLabel = new Insets(oben = 0, links = 10, unten = 6, rechts = 5);
    private final Insets insetsText = new Insets(oben = 0, links = 0, unten = 6, rechts = 10);
    private final Insets insetsUnten = new Insets(oben = 10, links = 0, unten = 10, rechts = 0);    
    
    public void LayoutGridBag()
    {
        // Top-Level-Container JFrame
        setTitle("Beispiel GridBagLayout");
        setSize(500,400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        // Container JPanel
        panel_grid = new JPanel(new GridBagLayout());
        add(panel_grid);
        // Bedienelemente dem JPanel hinzufügen.
        // Kopfzeile
        setStandardwerteGB();
        addGB(new JLabel("Anmeldeformular"), gridx = 1, gridy = 1,
                gridwidth = 2, gridheight, fill,
                weightx = 1.0, weighty, anchor,
                insets = insetsOben);
        //Grid-Reihe Name
        setStandardwerteGB();
        addGB(new JLabel("Name"), gridx = 1, gridy = 2,
                gridwidth, gridheight, fill,
                weightx, weighty, anchor = LINE_START,
                insets = insetsLabel);
        txtName = new JTextField();
        setStandardwerteGB();
        addGB(txtName, gridx = 2, gridy = 2,
                gridwidth, gridheight, fill = HORIZONTAL,
                weightx = 1.0, weighty, anchor,
                insets = insetsText);
        //Grid-Reihe Alter
        setStandardwerteGB();
        addGB(new JLabel("Alter"), gridx = 1, gridy = 3,
                gridwidth, gridheight, fill,
                weightx, weighty, anchor = LINE_START,
                insets = insetsLabel);
        txtAlter = new JTextField(3);
        txtAlter.setMinimumSize(txtAlter.getPreferredSize());
        setStandardwerteGB();
        addGB(txtAlter, gridx = 2, gridy = 3,
                gridwidth, gridheight, fill,
                weightx, weighty, anchor = LINE_START,
                insets = insetsText);
        //Grid-Reihe Kommentar
        setStandardwerteGB();
        addGB(new JLabel("Kommentar"), gridx = 1, gridy = 4,
                gridwidth, gridheight, fill,
                weightx, weighty, anchor = FIRST_LINE_START,
                insets = insetsLabel);
        txaKommentar = new JTextArea();
        setStandardwerteGB();
        addGB(new JScrollPane(txaKommentar), gridx = 2, gridy = 4,
                gridwidth, gridheight, fill = BOTH,
                weightx = 1.0, weighty = 1.0, anchor,
                insets = insetsText);
        //Grid-Reihe Boden
        btOk = new JButton("OK");
        setStandardwerteGB();
        addGB(btOk, gridx = 1, gridy = 5,
                gridwidth = 2, gridheight, fill,
                weightx, weighty, anchor,
                insets = insetsUnten);        
        
        setVisible(true);
    }
    
    /**
     * Setzen der GridBagConstraints - Argumente auf Standardwerte
     */
    private void setStandardwerteGB() 
    {
        // Standartwerte für GridBagConstraints
        gridx = RELATIVE;
        gridy = RELATIVE;
        gridwidth = 1;
        gridheight = 1;
        fill = NONE;
        weightx = 0.0;
        weighty = 0.0;
        anchor = CENTER;
        insets = new Insets(0, 0, 0, 0);
        ipadx = 0;
        ipady = 0;
    }    
    
    /**
    * 
    * @param component Bedienelement, welches dem GridBagLayout hinzugfügt wird
    * @param gridx x-Raster 
    * @param gridy y-Raster
    * @param gridwidth Breite des Elements (am Gridraster)
    * @param gridheight Höhe des Elements (am Gridraster)
    * @param fill Größenanpassung des Elements
    * @param weightx Verteilung im horizontalen Raum
    * @param weighty Verteilung im vertikalen Raum
    * @param anchor Verhalten, wenn Bedienelement kleiner als Anzeigebereich
    * @param insets Aussenabstände der Elemente
    */
    private void addGB(Component component,  int gridx,  int gridy, 
            int gridwidth, int gridheight, int fill,  double weightx,
            double weighty, int anchor, Insets insets) 
    {
        addGB(component, gridx, gridy, gridwidth, gridheight, fill, weightx,
                weighty, anchor, insets, ipadx, ipady);
    }      
    
    /**
     * Übergabe der GridBagConstraints-Argumente für die Bedienelemente mit 
     * Innenpolsterung
     * @param component Bedienelement, welches dem GridBagLayout hinzugfügt wird
     * @param gridx x-Raster 
     * @param gridy y-Raster
     * @param gridwidth Breite des Elements (am Gridraster)
     * @param gridheight Höhe des Elements (am Gridraster)
     * @param fill Größenanpassung des Elements
     * @param weightx Verteilung im horizontalen Raum
     * @param weighty Verteilung im vertikalen Raum
     * @param anchor Verhalten, wenn Bedienelement kleiner als Anzeigebereich
     * @param insets Aussenabstände der Elemente
     * @param ipadx Innenabstand Breite
     * @param ipady Innenabstand Höhe
     */
    private void addGB(Component component, int gridx,  int gridy,
             int gridwidth, int gridheight, int fill,  double weightx,  
             double weighty, int anchor,  Insets insets, int ipadx,  int ipady) 
        {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.gridx = gridx;
        constraints.gridy = gridy;
        constraints.gridwidth = gridwidth;
        constraints.gridheight = gridheight;
        constraints.fill = fill;
        constraints.weightx = weightx;
        constraints.weighty = weighty;
        constraints.anchor = anchor;
        constraints.insets = insets;
        constraints.ipadx = ipadx;
        constraints.ipady = ipady;
        panel_grid.add(component, constraints);
    }    
    
    public static void main(String[] args) 
    {
        LayoutGridBag gb = new LayoutGridBag();
        gb.LayoutGridBag();
    }
}

Variablendeklaration

Um sich einiges an Schreibarbeit zu ersparen bei diesem Layout, wird das hinzufügen der Elemente in unser Layout über Methoden realisiert. Hierfür deklarieren wir die notwendigen Variablen. Um ein vernünftiges Layout zu bekommen, verwenden wir die Klasse Insets. Hiermit geben wir an, wieviel Platz um das jeweilige Objekt sein muss. Ein Insets-Objekt erwartet 4 int-Werte als Parameter.

Insets(oben, links, unten, rechts );

GridBagConstraints

Diese Klasse wird beim GridBagLayout benötigt, um die entsprechenden Bedienelemente zu positionieren und die Werte für ihr Verhalten festzulegen. In diesem Beispielprogramm erfolgt die Zuweisung mit dem GridBagConstraints in Methoden. Wie in dem Programm zu ersehen ist, wird bei jedem Bedienelement als erstes eine Methode setStandardwerteGB() aufgerufen. Dies ist notwendig, da ansonsten fehlerhafte Variablenwerte den folgenden Methoden hinzugefügt werden könnten, wenn das nächste Bedienelement eben nicht die gleichen Werte hat.

Einem Element müssen die Werte des GridBagConstraints-Objektes zugewiesen werden, bevor sie dem Container hinzugefügt werden.

Um zu verstehen, was in diesem Layout passiert, ist im nachfolgenden Bild das Eingabeformular als Gitter (Grid = Gitter) dargestellt. Ähnlich wie das GridLayout arbeitet auch das GridBagLayout mit einer Gitterstruktur, allerdings dank des GridBagConstraints können wir im Gegensatz zum GridLayout unsere Bedienelemente richtig positionieren und ein gleichbleibendes Layout erzwingen.

Sehen wir uns nun die einzelnen Attribute der Klasse GridBagConstraints an.

gridx, gridy

Diese beiden Attribute dienen dazu, das Bedienelement an der richtigen Position im Gitter zu positionieren. Hierbei gilt, dass mit gridx die Spalte und mit gridy die Zeile angegeben wird, wo innerhalb des Gitters das Bedienelement positioniert werden soll.

Standardwert dieser beiden Attribute ist RELATIVE. Wird ein Bedienelement mit gridx = RELATIVE hinzugefügt, wird es rechts neben dem letzten Element platziert. Mit gridy = RELATIVE wird es unterhalb des letzten Elementes platziert. Dies ist allerdings in den seltensten Fällen erwünscht.

gridwith, gridheight

Mit diesen beiden Attributen kann die Größe im Gitter angegeben werden. Wenn wir in Tabellenkalkulationsprogrammen oder auch in Textverarbeitungsprogrammen mit Tabellen arbeiten, haben wir die Möglichkeit, aneinanderliegende Zellen zu verbinden, d. h. aus 2 oder mehr Zellen 1 Zelle zu machen. Nichts anderes machen diese beiden Attribute. Mit gridwith wird angegeben, wie breit (in Spalten) das Bedienelement im Layout sein soll. Mit gridheight wird angegeben, wie hoch (in Zeilen) das Bedienelement sein soll. Standardwert dieser beiden Attribute ist 1.

fill

Mit fill gegeben wir das "Füllverhalten" unseres Elementes an. D. h. wie verhält sich die Breite und Höhe des Bedienelementes, wenn wir die Fenstergröße ändern. In dem Beispielprogramm wurde z. B. für das Textfeld für die Namenseingabe und für die Textarea für die Kommentare ein fill angegeben, was dafür sorgt, dass das Textfeld breiter wird, wenn wir die Breite unseres Fensters ändern. Der Standardwert für Fill ist NONE. Neben NONE stehen uns noch weitere Konstanten zur Verfügung.
Konstante Beschreibung
NONE Mit dem Standardwert wird dem Element eine Größenänderung untersagt.
HORIZONTAL Bei dieser Einstellung bleibt die Höhe des Elements unverändert. Die Breite passt sich horizontal an. (Das Textfeld für den Namen in unserem Beispiel)
VERTICAL Die Breite des Elements bleibt unverändert. Die Höhe passt sich allerdings vertikal an.
BOTH Bei Änderung der Fenstergröße passt sich das Element vertikal und horizontal an. (Die Textarea für Kommentare in unserem Beispiel)

weightx, weighty

Damit die Komponenten nicht im Zentrum des Panels gebündelt bleiben, sondern sich über die zur Verfügung stehende Fläche verteilen können, müssen wir dem Layout sagen, wie der zusätzliche Platz verteilt werden soll. Das geschieht mit weightx zum Verteilen des horizontalen Raums und weighty zum Verteilen des vertikalen Raums. Abhängig hierfür ist die Anzahl der Elemente innerhalb der Zeile oder Spalte.

anchor

Dieses Atribut wird verwendet, wenn die Komponente kleiner ist als ihr Anzeigebereich. Damit bestimmen wir, wo die Komponente innerhalb ihrer Anzeigefläche platziert wird. Standardwert ist CENTER.

Damit es nicht zu einfach ist, haben wir grundsätzlich 3 verschiedene Möglichkeiten, den Anzeigebereich zu bestimmen.
  • Absolute Position (seid JDK 1.1)
  • Orientierungsbezogene Position (seid JDK 1.4 - Orientierung an Komponentenorientierung im Container (RIGHT_TO_LEFT, LEFT_TO_RIGHT))
  • BASELINE-bezogene Position (seid JDK 1.6 bezieht sich auf die BASELINE der Elemente innerhalb einer Zeiler)
Für uns relevant sind die Absoluten und Orientierungsbezogenen Positionen. Nachfolgende Grafik zeigt die Positionierung mit den Konstanten in einer Zelle.

insets

Hier wird der Mindestabstand zwischen dem Bedienelement und dem Anzeigebereich angegeben. Standardwert ist insets(0,0,0,0).

ipadx, ipady

Mit diesen beiden Attributen können wir einen Raum festlegen, welchem der Komponente hinzugefügt wird. ipadx verändert die Breite, ipady die Höhe. Standardwert für beide Attribute ist jeweils 0.

Um den Unterschied zwischen insets, ipadx und ipady zu verdeutlichen, habe ich eine kleine Grafik erstellt, welche ein Element innerhalb einer Zelle darstellt.

nach oben Java4Beginners -- Seitenversion 1.0 -- Stand: 2017-05-18