Eine Einführung in den Layout-Manager TableLayout

Table Dancer

Steffen Müller

Viele Entwickler werden beim Erstellen von GUI-Applikationen durch die Vielfalt und Komplexität von Javas Standard-Layout-Managern abgeschreckt. Oft wird aus diesem Grund auf Microsoft-Technologien oder auf das Web ausgewichen. Doch dies ist nicht nötig: Der Layout-Manager TableLayout ermöglicht es, Swing-GUIs äußerst effizient und nach dem Prinzip von HTML-Tabellen bzw. Tabellenkalkulationen zu erzeugen.

Sun stellt für die Entwicklung von Java-GUIs sechs verschiedene Layout-Manager zur Verfügung: FlowLayout, GridLayout, BorderLayout, CardLayout, GridBagLayout und BoxLayout. Jeder Layout-Manager funktioniert nach einem anderen Prinzip und ist unterschiedlich komplex. Meist ist es nötig, verschiedene Layout-Manager zu kombinieren, indem einige Panels ineinander verschachtelt werden. Dies macht die jeweiligen Java-Klassen jedoch sehr unübersichtlich und schwer wartbar. Einzig der Layout-Manager GridBagLayout ist flexibel genug, um Verschachtelungen zu vermeiden. Doch hier wird Flexibilität mit Komplexität bezahlt: Die benötigte Einarbeitungszeit ist vergleichsweise hoch.

Somit ist die Versuchung groß, die Client-Applikation unter Zeitdruck doch besser schnell mit Visual Basic zusammenzuklicken – unter Verzicht auf die Vorteile von Java. Oder man weicht gleich auf das gute alte Web aus, wobei dies oftmals nicht die optimale Lösung ist, da diese starre Technologie nicht interaktiv genug ist.
Abhilfe schafft der Layout-Manager TableLayout von Daniel Barbalace [1]. Er bietet die Flexibilität und die Leistung von GridBagLayout, ist aber weitaus einfacher zu verwenden. Das Konzept von TableLayout orientiert sich an HTML-Tabellen bzw. Tabellenkalkulationen: Der Entwickler hat die Möglichkeit, Spalten und Zeilen zu definieren und in den Zellen Swing-Komponenten zu platzieren. Im Vergleich zu HTML-Tabellen ist es dabei möglich, alle Spalten- und Zeilen-Abmessungen sowie die Ausrichtung der enthaltenen Komponenten exakt festzulegen. Außerdem kann das Verhalten bei einer Veränderung der Fenster-/Dialog-Größe bestimmt werden.
TableLayout ist standardmäßig nicht in Suns J2SE enthalten, kann aber sowohl für nicht-kommerzielle als auch für kommerzielle Projekte kostenlos verwendet werden. Die aktuelle Version kann über das Web heruntergeladen werden. Es muss lediglich die JAR-Datei in den Classpath eingetragen werden, dann schon kann es losgehen.

Abb. 1: Ein mit TableLayout erstelltes Gitter, in dem JButtons platziert wurden

Wie bei einer Tabellenkalkulation wird bei TableLayout eine Zelle im Format [Spalte, Zeile] angesprochen, wobei die Zählung der Spalten und Zeilen jeweils bei 0 beginnt. Die Zelle [0, 2] ist also in Spalte 0, Zeile 2 zu finden.
Ein weiterer wichtiger Punkt ist die Festlegung der Zellen-Größe sowie des Resize-Verhaltens. Hier wurden wiederum einige Konzepte von HTML übernommen. Die Spalten- bzw. Zeilengröße kann bestimmt werden durch:

  • Eine Angabe in Pixeln (Angabe mittels positiver ganzer Zahl; z.B. 75 entspricht 75 Pixeln).
  • Die Höhe/Breite der darin enthaltenen Swing-Komponente (Angabe als Konstante: TableLayout.PREFERRED).
  • Eine Angabe in Prozent (Angabe mittels positiver reeller Zahl zwischen 0 und 1; z.B. 0.25 entspricht 25% des noch verfügbaren Platzes).
  • Die Zuweisung des restlichen Platzes (Angabe als Konstante: TableLayout.FILL).

Dabei wird die Zuweisung des vorhandenen Platzes in genau dieser Reihenfolge vorgenommen. Die in Abpictureung 1 dargestellte Beispiel-Applikation soll dies verdeutlichen. Die Breite des Fenster-Inhalts wurde auf 640 Pixel und die Höhe auf 480 Pixel festgelegt. Tauchen wir gleich in den Code ein zu der Stelle, wo die Spalten und Zeilen festgelegt werden (Listing 1).

Listing 1

// Set the column and row dimensions and behaviors
// Columns
double sizes[][] = {{75, 150, 0.3, 0.4, TableLayout.FILL,
// Rows
{80, TableLayout.PREFERRED, TableLayout.FILL}};

// Get the contentPane and set TableLayout to be the layout manager
Container content = getContentPane();
content.setLayout(new TableLayout(sizes));

Wie aus dem Beispiel-Code ersichtlich ist, werden zuerst alle Spalten und anschließend alle Zeilen in einem zweidimensionalen Array abgelegt. Dieses kann dann beim Aufruf des TableLayout-Konstruktors direkt als Parameter angegeben werden.
Die höchste Priorität haben Angaben in Pixel – im Beispiel also die ersten beiden Spalten: Spalte 0 und Spalte 1. Somit bleiben von den 640 Pixeln noch 415 Pixel übrig, die auf die Spalten mit Prozent-Angaben verteilt werden – die Spalten 2 (415 * 30% = 124,5; gerundet 125 Pixel) und 3 (415 * 40% = 166 Pixel). Als letztes wird der restliche Platz an die Spalte mit der TableLayout.FILL-Konstante vergeben (124 Pixel).
Bei den Zeilen gestaltet sich die Vergabe des Platzes in unserem Beispiel etwas einfacher: Zeile 0 hat die höchste Priorität und erhält 80 Pixel. Zeile 1 erhält anschließend so viel Platz, wie die darin platzierte Komponente verlangt. Im Falle unseres Buttons sind dies 25 Pixel. Die übrigen 375 Pixel bekommt Zeile 2. Somit wurden die verfügbaren Pixel, wie in den Tabellen 1 und 2 dargestellt, verteilt.
Wird die Größe des Applikations-Fensters durch den Benutzer geändert, wird die Platzvergabe nach obigen Schema neu berechnet.

Zeile Höhe in Pixel
0 80
1 25
2 375
Gesamt 480

Ein mit TableLayout erstelltes Gitter ist nicht sichtbar. Es verhält sich in dieser Hinsicht wie eine HTML-Tabelle, bei der das border-Attribut auf 0 gesetzt wurde. Aus diesem Grund ist es sehr hilfreich, das Gitter – Sinnvollerweise vor der Codierung – auf einem Blatt Papier aufzuzeichnen.
Nachdem die Swing-Komponenten erzeugt worden sind, können sie mit der add(Component component, Object constraints)-Methode auf dem Gitter platziert werden. Listing 2 zeigt, wie unserem Beispiel die JButtons hinzugefügt werden.

Listing 2

// Add the components to the table
content.add(button01, "0, 0");
content.add(button02, "1, 0, 1, 1");
content.add(button03, "2, 0");
content.add(button04, "3, 0");
content.add(button05, "4, 0");
content.add(button06, "0, 1");
content.add(button07, "2, 1");
content.add(button08, "3, 1");
content.add(button09, "4, 1");
content.add(button10, "0, 2");
content.add(button11, "1, 2");
content.add(button12, "2, 2, 4, 2");

Um TableLayout anzuweisen, wo es die jeweiligen Komponenten platzieren soll, werden im zweiten Parameter innerhalb eines Strings die entsprechenden Koordinaten angegeben. Dabei ist wiederum die Reihenfolge [Spalte, Zeile] zu beachten.
Nun ist es auch möglich “ wie mit den aus HTML bekannten colspan– und rowspan-Befehlen “ Zellen über mehrere Spalten und/oder mehrere Zeilen zu verbinden. Dies ist in Listing 2 bei zwei Zellen der Fall. button02 erstreckt sich vertikal über die Zellen [1, 0] und [1, 1], und button12 zeigt diese Möglichkeit horizontal mit den drei Zellen [2, 2] und [3, 2] und [4, 2].
Soll sich eine Swing-Komponente über mehrere Spalten und/oder Zeilen erstrecken, gibt man zuerst die Koordinaten der obere linke Ecke (bei button02 beispielsweise 1, 0) und anschließend die Koordinaten der unteren rechten Ecke an. Da es bei button02 kein links und rechts gibt, wird zuerst die obere Koordinate, dann die untere Koordinate verwendet. Bei button12 wurde einfach zuerst die linke und dann die rechte Zelle angegeben, da kein oben und unten existiert.
Standardmäßig wird jede Komponente auf die Größe ihrer Zelle skaliert. Ist der Zellen-Inhalt eigentlich jedoch kleiner als die Zelle, bietet TableLayout die Möglichkeit, Inhalte innerhalb einer Zelle auszurichten – ähnlich wie bei Tabellen in Textverarbeitungs-Applikationen oder Tabellenkalkulationen. Entwicklungstechnisch ist auch dies kein Hexenwerk: Wollen wir beispielsweise den JButton button03 auf seine Original-Größe (Preferred Width und Preferred Height) bringen und ihn dann unten mittig in seiner Zelle ausrichten, verwenden wir einfach folgende weitere Parameter:

Abb. 2: Zelle [2, 0] wurde unten mittig ausgerichtet.

Dabei steht c für center und b für bottom (Abb. 2). Der dritte Parameter innerhalb der Zeichenkette setzt das horizontale Ausrichtungs-Attribut, und der vierte Parameter das vertikale. Tabellen 3 und 4 zeigen alle Ausrichtungs-Attribute.

Attribut Bedeutung
f Volle Ausdehnung (Standard)
t Obere Ausrichtung
c Mittige Ausrichtung
b Untere Ausrichtung

Im nächsten Java Magazin lernen Sie in einem Workshop die Vorgehensweise von der Konzeption bis zur Entwicklung eines modernen Java User-Interfaces mit Hilfe von TableLayout am Beispiel eines Formulars kennen. Der Workshop enthält u.a. auch ein kleines Framework, das die GUI-Entwicklung erleichtert.

Fragen an Daniel Barbalace

Java Magazin: Herr Barbalace, was hat Sie dazu bewogen, TableLayout zu entwickeln?

Daniel Barbalace: Ich wollte einen Layout-Manager haben, der die Funktionalität von GridBagLayout bietet, aber einfacher zu verwenden ist. Auch der Code für GUIs sollte einfacher und lesbarer sein. Außerdem wollte ich Komponenten dynamisch hinzufügen und entfernen können, ohne dabei andere Komponenten zu beeinflussen. Da es so einen Layout-Manager nicht gab, habe ich einen entwickelt.

JM: Wie verhält sich die Performance von TableLayout im Vergleich zu den Layout-Managern von Sun?

Barbalace: Da Layout-Manager lediglich Größe und Position von Komponenten bestimmen, kann der Flaschenhals einzig in den Berechnungen liegen. Bei meiner letzten Messung bei einem relativ komplexen GUI hat TableLayout für die Berechnungen Zeiten im Millisekunden-Bereich benötigt. Die verwendeten Algorithmen enthalten einige verschachtelte Schleifen. Beim Einsatz von bis zu ca. 100 AWT-/Swing-Komponenten sollte die Berechnungszeit jedoch nicht bemerkbar sein. Übrigens cachet TableLayout seine Berechnungen, sodass sie nur dann vorgenommen werden, wenn dies unbedingt nötig ist.

JM: Gibt es Pläne von Sun, TableLayout in zukünftige JDKs zu integrieren, um Entwicklern das Erstellen von GUIs zu erleichtern?

Barbalace: Ich habe Sun bereits die Erlaubnis gegeben, TableLayout in alle JDKs unter Suns eigener Lizenz einzubinden. Sun hat TableLayout zwar einmal auf seiner Web-Site vorgestellt, aber in Bezug auf die Einbindung scheint Sun bisher kein Interesse zu haben. Das Unternehmen hat jedoch weiterhin meine Erlaubnis.

Geschrieben von
Steffen Müller
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.