![]() |
|
Funktionale Komponenten von Benutzerschnittstellen Die Komponenten von Benutzeroberflächen, die bis zu diesem Punkt des Buches betrachtet wurden, sind Grafiken, Bilder, Animationen und Audio. Neben diesen Grundbestandteilen machen aber gerade Bausteine wie Buttons oder Texteingabefelder die eigentliche Stärke einer Benutzeroberfläche aus. Die Darstellung dieser Komponenten ist Gegenstand dieses Unterkapitels. Hierzu muss zunächst das AWT näher betrachtet werden. Bis zu diesem Zeitpunkt wurden AWT-Funktionen eher implizit verwendet, indem die zur Umsetzung der beschriebenen Aufgaben notwendigen Klassen verwendet wurden. Diese Sicht reicht allerdings für die im Folgenden zu beschreibenden Komponenten nicht mehr aus. Abstract Windowing Toolkit (AWT) Das Abstract Windowing Toolkit (AWT) ist eine Menge von Klassen, mit deren Hilfe grafische Benutzeroberflächen einschließlich der Interaktionsmöglichkeiten, die sich aus der Verwendung der Maus und der Tastatur ergeben, erstellt werden können. Da die Sprache Java plattformunabhängig ist, stellt das AWT Möglichkeiten zur Verfügung, eine Benutzerschnittstelle zu entwickeln, die dieselbe Funktionalität und dasselbe Aussehen auf allen Systemen bietet, auf der sie ausgeführt wird. Bedenkt man allerdings, dass heutzutage auf vielen Browsern Spezialeinstellungen benutzt werden können, so ist diese Forderung nur dann zu realisieren, wenn Java-Applets in Plug-Ins ablaufen. Anderenfalls müssen die Funktionalität und auch das Aussehen einer Anwendung „von Hand" auf möglichst vielen unterschiedlichen Systemen getestet werden, um tatsächlich eine Einheitlichkeit garantieren zu können. Die Klassen des AWTs sind Teil des Packages java.awt. Werden daher Benutzerschnittstellen implementiert, so darf die Importierung dieses Packages nicht vergessen werden. Die statischen Komponenten einer Benutzerschnittstelle bestehen aus den folgenden drei Komponenten:
Die in Java möglichen Komponenten und Container werden im Folgenden beschrieben. Hierzu wird der Begriff Benutzerschnittstelle mit dem Kürzel GUI (Graphical User Interface) bezeichnet. Komponenten und Container Komponenten werden in einem GUI verwendet, indem sie in einen Container integriert werden. Container können hierbei selbst als Komponenten aufgefasst werden und in übergeordneten Containern platziert werden. Der Aufbau eines GUIs aus Containern ist mit dem Erstellen eines Layouts identisch (siehe Eine Komponente wird in einem Container platziert, indem sie zunächst erstellt wird und anschließend mit Hilfe der add-Methode dem Container zugefügt wird. Da ein Applet gleichzeitig ein Container ist, ist die Verwendung der add-Methode innerhalb von Applets zulässig.
Abb. 4.15: GUI-Komponenten Das Hinzufügen einer Komponente zu einem Container stellt diese Komponente selbst nicht notwendigerweise sofort dar. Eine Anzeige erfolgt erst dann, wenn die paint-Methode des Containers aufgerufen wird. Dies kann beschleunigt werden, indem die repaint-Methode in der Implementierung verwendet wird. Komponenten eines GUIs werden am besten in der init-Methode beschrieben. Da sich die Elemente eines GUIs im Programmablauf normalerweise nicht verändern, kann der Aufbau des GUIs bereits in der Initialisierungsphase erfolgen. Im Folgenden werden die Komponenten, die neben Grafiken, Text, Bildern, Audio und Animationen in einem GUI verwendet werden können, erläutert. Das in Abb. 4-15 angegebene Schaubild erläutert den Zusammenhang der hierbei verwendeten Klassen. Textmarken (Labels) Textmarken (Labels) werden üblicherweise dazu verwendet, den Zweck anderer Komponenten zu spezifizieren. Die Verwendung von Labels ist der bereits erläuterten Methode drawString aus zwei Gründen stets vorzuziehen. Erstens wird das Layout einer Textmarke automatisch gesetzt, während Texte, die mit drawString erzeugt werden, stets an einer festen Position erscheinen. Zweitens werden Labels nach ihrer Generierung automatisch dargestellt und müssen daher nicht explizit in der paint-Methode angegeben werden. Zur Definition eines Labels können die folgenden Konstruktoren verwendet werden:
Nachdem ein Label erzeugt wurde, kann der Font mit der bereits beschriebenen Methode setFont verändert werden. Erfolgt dies innerhalb eines Applets, so werden die Eigenschaften aller Textmarken verändert. Innerhalb des Labels wird nur der Font der Textmarke selbst verändert. Nachdem eine Textmarke erzeugt wurde, kann ihr Text mit der Methode setText(String) verändert werden. Dies ist vor allem nötig, wenn Inhalte dynamisch verändert werden sollen oder wenn der Konstruktor Label() verwendet wurde, da dann das Label anfangs keinen Text darstellt. Zur Abfrage des Inhalts einer Textmarke wird die Methode getText() verwendet, zur Veränderung der Textausrichtung die Methode setAlignment, die einen Parameter der Form Label.LEFT, Label.CENTER oder Label.RIGHT erwartet. Das folgende Beispiel stellt die Verwendung von Textmarken dar.
import java.applet.*; Label links = new Label("Schiffe versenken", Label.LEFT); setFont(f); } } Die Ausgabe dieses Beispiels ist in Abb. 4-16 dargestellt. Es mag überraschend erscheinen, dass die Ausgabe nicht in drei Zeilen erscheint. Da aber kein spezielles Layout angegeben wurde, werden die Komponenten so angeordnet, dass möglichst wenig Platz verbraucht wird. In diesem Fall sind die links- bzw. rechtsbündig angeordneten Texte in einer Zeile darstellbar.
Abb. 4.16: Textmarken Knöpfe (Buttons) Knöpfe (Buttons), die mit der Maus bedienbar sind, werden mit Hilfe der Klasse Button mit den folgenden Konstruktoren erzeugt:
Nach der Generierung eines Button-Objekts kann der Textinhalt mit der Methode
import java.applet.*; Button start = new Button("Beginne Spiel"); ende.setLabel("Beende Spiel"); } }
Abb. 4.17: Knöpfe Checkboxen Checkboxen werden verwendet, um Optionen eines Programms auszuwählen. Die Auswahl ist hierbei nicht exklusiv, es können also auch mehrere Optionen ausgewählt sein. Soll genau eine Option auswählbar sein, so werden Radiobuttons verwendet, die im Folgenden erklärt werden. Der Name Radiobutton ist abgeleitet von alten Radios, bei denen das Drücken eines Knopfs bewirkte, dass ein anderer gedrückter Knopf wieder heraussprang. Checkboxen werden mit den folgenden Konstruktoren erzeugt:
Die Verwendung dieser Konstruktoren entspricht exakt der Benutzung der Konstruktoren von Buttons. Der Anfangszustand der so erzeugten Boxen ist immer die nicht ausgefüllte Box. Dieser Zustand kann aber mit der Methode
import java.applet.*; Checkbox audio = new Checkbox("Ton ausschalten"); audio.setState(true); } }
Abb. 4.18: Checkboxen Radiobuttons Ein Radiobutton ist eine spezielle Art von Checkbox, in der immer nur eine der Optionen ausgewählt sein kann. Hierzu wird ein CheckboxGroup-Objekt mittels der Methode CheckboxGroup() angelegt. Anschließend werden die Bestandteile, die Radiobuttons, mit der folgenden Syntax hinzugefügt:
Checkbox(String, boolean, CheckboxGroup) Hierdurch wird eine Checkbox mit dem durch das boolean-Argument spezifizierten Zustand erzeugt, die zur CheckboxGroup gehört, die durch den dritten Parameter angegeben ist. Das folgende Beispiel, dessen Ausgabe in Abb. 4-19 dargestellt ist, verdeutlicht diese Funktion. Weiterhin kann die Methode setCurrent(Checkbox) dazu verwendet werden, die Auswahl auf die Checkbox zu setzen, die hierbei als Parameter übergeben wird. Weiterhin kann die Methode getCurrent() dazu eingesetzt werden, die momentan selektierte Checkbox abzufragen.
Abb. 4.19: Radiobuttons
import java.applet.*; CheckboxGroup level = new CheckboxGroup(); add(l1); } } Auswahllisten Mittels Auswahllisten kann ein Element eines aufklappbaren Menüs selektiert werden. Hierzu wird in Java die Klasse Choice verwendet. Der Aufbau einer Auswahlliste besteht aus zwei Schritten: Zuerst wird ein Choice-Objekt mittels des Konstruktors Choice() erzeugt, anschließend werden die Menüeinträge mittels der Methode add(String) hinzugefügt. Das folgende Beispiel, dessen Ausgabe in Abb. 4-20 dargestellt ist, verdeutlicht diese Funktion.
import java.applet.*; Choice liste = new Choice(); liste.add("Schiffe setzen"); } }
Abb. 4.20: Auswahllisten Zur Kontrolle einer Auswahlliste stehen zusätzlich die folgenden Methoden als Teil der Klasse Choice zur Verfügung:
Textfelder Im Gegensatz zu Textmarken sind Textfelder vom Benutzer veränderbar. Hierzu steht in Java die Klasse Oftmals ist es von Bedeutung, Felder zu verwenden, die der Benutzer verändern kann, ohne die eingetippten Buchstaben lesen zu können. Dies erfolgt typischerweise bei Passwort-Eingaben, wobei die Eingabe hierbei als Stern dargestellt wird. In der Klasse
import java.applet.*; Label benutzerLabel = new Label("Benutzername"); add(benutzerLabel); } }
Abb. 4.21: Textfeld Auch die Klasse TextField stellt eine Menge an Routinen zur Verfügung, mit deren Hilfe Textfelder kontrolliert werden können:
Textbereiche Textbereiche, die mittels der Klasse Das folgende
import java.applet.*; String inhalt = String inhalt = "Textbereiche, die mittels der Klasse TextArea "+ "erstellt werden, \nkoennen groessere Textmengen aufnehmen, als"+ "Textfelder. Hierzu stehen horizontale \nund vertikale " + "Schiebebalken zur Verfuegung, die den \nAnzeigebereich geeignet" + " verschieben koennen. Ein Textbereich \nwird mit den " + "folgenden Konstruktoren angelegt:"; textFeld = new TextArea(inhalt, 3, 20); } }
Abb. 4.22: Textbereich Auch für Textbereiche stehen zusätzliche Kontrollmethoden zur Verfügung:
Sowohl Textfeld als auch Textbereich erben von der Klasse TextComponent. Die Methoden setText, getText, setEditable und isEditable, die im Kontext von Textfeldern erläutert wurden, stehen daher auch für Textbereiche zur Verfügung. Frames (Fenster) Frames (Fenster) für Applications und Applets werden mit der Klasse Frame realisiert. Jede Application, die mit einem GUI arbeiten will, benötigt mindestens ein Frame-Objekt. Anstelle der Frames sollte allerdings für Applications ein Dialog-Objekt (siehe im Anschluss an diesen Abschnitt) verwendet werden, wenn eine Fensterabhängigkeit realisiert werden soll. Fenster sind bspw. dann voneinander abhängig, wenn ein Fenster unsichtbar werden soll, wenn ein anderes ikonifiziert wird. Applets können derartige Mechanismen nicht verwenden, da Dialoge aufgrund von Sicherheitsrestriktionen von Applets nur mit Einschränkungen verwendet werden können. Bei Applets sollten daher grundsätzlich Frames verwendet werden. Die Klasse Frame kann mit zwei Konstruktoren aufgerufen werden:
Weiterhin stellt Der folgende
import java.awt.*; public Fenster() { } Fenster fenster = new Fenster(); } } Dialogfenster Mittels der Klasse Dialog bietet das AWT die Möglichkeit, Dialogfenster zu erzeugen. Dialogfenster treten niemals selbstständig auf, sondern immer nur in Zusammenhang mit einer Anwendung. Sie sind daher auch stets von anderen Fenstern derart abhängig, dass bspw. auch Dialogfenster verschwinden, wenn die Fenster, von denen Dialogfenster abhängen, ikonifiziert werden,. Als Subklasse der Klasse Dialog kann auch die Klasse FileDialog verwendet werden, die ein Dialogfenster zum Selektieren von Dateien erzeugt. Hierbei besteht aber eine prinzipielle Schwierigkeit: Aufgrund des Sicherheitsmodells von Java kann ein Dialog im Standardbetrieb nur dann verwendet werden, wenn die Anwendung nicht in Form eines Applets implementiert ist. Dies liegt unter anderem auch daran, dass keine Möglichkeit besteht, dass Applets das Fenster, in dem sie ablaufen, identifizieren können. Eine Verknüpfung mit einem Dialogfenster ist daher unmöglich. Eine Ausnahme hierzu sind Applets, die eigene Fensterimplementierungen (Frames) realisieren. Erstellt ein derartiges Applet ein Fenster, aus dem ein Dialog aufgerufen wird, so ist dies unter Einhaltung der Sicherheitsrestriktionen zulässig. Dialoge können die Aufmerksamkeit des Anwenders erregen (exklusive Funktion), da andere Tätigkeiten solange verhindert werden können, bis ein Dialogfenster geschlossen wird. Standardmäßig ist dieses Verhalten allerdings nicht eingestellt, so dass ein Benutzer auch ohne weiteres in anderen Fenstern der Anwendung arbeiten kann, während ein Dialogfenster geöffnet ist. Die Klasse Dialog beinhaltet die folgenden Methoden:
Das folgende Beispiel erzeugt ein Dialogfenster, das innerhalb einer Application aufgerufen wird. Es ist zu beachten, dass die notwendige Funktionalität der Knöpfe an dieser Stelle nicht Teil des Beispielprogramms ist, da die notwendige Erklärung erst in
import java.awt.*; private TextArea textArea; textArea = new TextArea(5, 40); } DialogFenster fenster = new DialogFenster(); } } RealDialog(Frame dw, String titel) { super(dw, titel, false); } }
Abb. 4.23: Dialogfenster Menüs Menüs sind mit Auswahllisten vergleichbar, jedoch wesentlich umfassender. Im Gegensatz zu den bisher erläuterten Komponenten erben Menüs ihre Funktionalität nicht von der Klasse Component, da einige Plattformen Menüs schwerwiegende Einschränkungen auferlegen. Menüs erben ihre Funktionalität daher von der Klasse MenuComponent. Im AWT stehen die folgenden Subklassen der Klasse MenuComponent zur Unterstützung von Menüs zur Verfügung:
MenuBar repräsentiert die plattformabhängige Zuweisung einer Gruppe von Menüs an ein Fenster. Um ein MenuComponent-Objekt enthalten zu dürfen, muss ein Objekt das Interface MenuContainer implementieren. Derzeit sind die Klassen Frame, Menu und MenuBar die einzigen, die innerhalb des AWTs das Interface MenuContainer implementieren. Das folgende Beispiel, das als Application implementiert ist, demonstriert die Verwendung von Menüs (siehe Abb. 4-24). Hierzu ist zunächst eine Klasse zu implementieren, die die Klasse Frame erweitert (mit Frames werden Fenster implementiert). In der main-Methode muss noch die Größe des Fensters gesetzt werden, ebenso wie die Sichtbarkeit des Fensters mittels setVisible. Zur Einrichtung eines MenuBar-Objekts wird zunächst der Konstruktor MenuBar aufgerufen, der mittels setMenuBar aktiviert wird. Anschließend wird diesem MenuBar-Objekt ein Menü zugefügt, wozu der Konstruktor Menu(String, boolean) verwendet wird. Der Parameter gibt hierbei den Namen des Menüs an. Im nächsten Schritt wird mittels add das Menü dem MenuBar-Objekt hinzugefügt. Die Menüeinträge werden anschließend unter Angabe ihres Namens erzeugt und dem Menü hinzugefügt.
import java.awt.*; public MenuWindow() { MenuBar mb; } MenuWindow window = new MenuWindow(); } }
Abb. 4.24: Menüs Scroll-Listen Scroll-Listen ähneln den bereits beschriebenen Auswahllisten. Im Unterschied dazu kann aber in einer Scroll-Liste mehr als ein Element gleichzeitig ausgewählt werden. Weiterhin werden Scroll-Listen nicht vollständig angezeigt. Anstelle dessen wird ein vertikaler Schiebebalken dazu verwendet, den Anzeigebereich der Listenelemente zu variieren. Zur Erzeugung einer Scroll-Liste stehen die folgenden Konstruktoren in der Klasse List zur Verfügung:
Nachdem ein
import java.applet.*; List liste = new List(2, true); liste.add("Schiffe setzen"); } }
Abb. 4.25: Scroll-Liste Auch Scroll-Listen können Methoden verwenden, die bereits im Zusammenhang mit Auswahllisten definiert wurden: getItem, getItemCount, getSelectedIndex, getSelectedItem und select. Des Weiteren können die folgenden Kontrollroutinen verwendet werden:
Scrollbars und Slider Scrollbars sind Komponenten, die die Auswahl eines Werts derart ermöglichen, dass ein Schiebebalken horizontal oder vertikal mit der Maus bewegt werden kann. Die Position, an der sich der Schiebebalken befindet, repräsentiert den Wert einer Variablen. Ein Beispiel für die Verwendung einer Scrollbar ist das Setzen der Geschwindigkeit der Maus. Zur Generierung einer Scrollbar werden üblicherweise der minimale und der maximale Wert angegeben, die die Auswahl begrenzen. Hierzu stehen die folgenden Konstruktoren zur Verfügung:
Das folgende
import java.applet.*; Scrollbar sbar = new Scrollbar(Scrollbar.HORIZONTAL, 300, 0, 0, 1000); add(sbar); } }
Abb. 4.26: Scrollbar Die Werte in Scrollbars können mit Hilfe der Methode getValue() abgefragt werden und mittels der Methode setValue(int) gesetzt werden. Canvas-Objekte Canvas-Objekte werden in einem GUI dazu eingesetzt, Bilder oder Animationen anzuzeigen. Um die Klasse Canvas verwenden zu können, muss eine Subklasse erzeugt werden, die dann bspw. Zeichenoperationen innerhalb der überschriebenen paint-Methode durchführt. Wurde eine derartige Subklasse erzeugt, so kann der Konstruktor dieser Klasse aufgerufen werden und mittels new einem Container ein neues Canvas-Objekt hinzugefügt werden. Das folgende Beispiel, dessen Ausgabe in Abb. 4-27 dargestellt ist, zeigt die Verwendung dieser Klasse auf. Hierbei wird ein Kreuz in der Mitte eines Fensters erzeugt, dessen Position verändert wird, wenn sich die Größe des Fensters ändert. Die hierzu notwendige Layout-Angabe ist Thema des folgenden Unterkapitels.
import java.applet.*; GridLayout g = new GridLayout(1,1); setLayout(g); } } public void paint(Graphics screen) { int x = getSize().width/2; } }
Abb. 4.27: Canvas-Beispiel Panels Die vorgestellte Klasse Canvas wird hauptsächlich dazu eingesetzt, eine Oberfläche für Bild- und Grafikelemente zu bieten. Im Gegensatz dazu dient die Klasse Panel als allgemeine Container-Subklasse. In einem Panel können daher Komponenten gruppiert werden, aber auch spezielle Funktionen definiert werden, die auf den Komponenten arbeiten, die Teil eines Panels sind. Eine häufig vorzufindende Anwendung derartiger Funktionen sind bspw. solche, die eine spezielle Event-Verarbeitung für eine Menge von Komponenten zur Verfügung stellen. Aufgrund dieser Logik ist es kaum überraschend, dass die Klasse Applet eine Subklasse von Panel ist. Die speziellen Funktionen, die mittels der Klasse Applet zur Verfügung gestellt werden, betreffen die Abarbeitung in Browsern oder im Appletviewer. Hierbei besteht auch die Möglichkeit, dass eine Anwendung sowohl als Application als auch als Applet definiert wird. In diesem speziellen Fall enthält das Programm eine Applet-Subklasse, die dann nicht verwendet wird, wenn das Programm als Application ausgeführt wird. Es ist nun auch verständlich, warum Komponenten mittels der add-Methode zu Applets hinzugefügt werden konnten, ohne explizit ein Panel zu verwenden. Da Applets ja eine Subklasse der Klasse Panel sind, wurde die letztere Klasse bisher implizit benutzt. Die Verwendung der Klasse Panel ist einfach: Es wird zunächst mittels des Konstruktors Panel() ein leeres Panel erzeugt, dem anschließend mittels der add-Methode Komponenten hinzugefügt werden. Hierzu wird der Name des Panels, ein Punkt und die add-Methode mit dem Parameter, der die hinzuzufügende Komponente angibt, verwendet. Diese Syntax entspricht vollständig der, die auch bisher zur Adressierung von Subelementen einer Klasse verwendet wurde. Das folgende Beispiel demonstriert dieses Vorgehen, indem ein Panel angelegt wird, das drei Buttons enthält.
import java.applet.*; Panel p = new Panel(); p.add(b1); } }
Abb. 4.28: Canvas-Beispiel Layout-Management In der Erläuterung der Komponenten, die Teil eines GUIs sein können, wurde bereits deutlich, dass zu deren Anordnung die Angabe eines Layouts erforderlich ist. Die Hauptaufgabe eines Layouts besteht daher darin, die relative Position einer Komponente in Relation zu anderen Bestandteilen einer Benutzerschnittstelle festzulegen. In Java können das Flow-Layout, das Grid-Layout, das Border-Layout, das Card-Layout und das GridBag-Layout in Abhängigkeit vom Aussehen, das eine Anwendung haben soll, verwendet werden. Die folgenden Abschnitte erläutern die Verwendung der verschiedenen Layout-Möglichkeiten und beleuchten die Verwendung eines bestimmten Layouts in Abhängigkeit vom Typ der Anwendung, die erstellt werden soll. Zur Vergleichbarkeit der verschiedenen Möglichkeiten wird jeweils dasselbe Anwendungsbeispiel, die Benutzerschnittstelle des Spiels „Schiffe versenken", herangezogen. Flow-Layout Das Flow-Layout stellt die einfachste Layout-Möglichkeit in Java dar. Aus diesem Grund wird auch stets dieses Layout verwendet, wenn keine andere Auswahl spezifiziert wird. Allgemein wird zur Definition eines Layouts ein Layout-Manager verwendet, dessen Aufgabe die Anordnung der Komponenten eines GUIs ist. Diese Vorgehensweise ist in allen Layouts gleich. Das Flow-Layout, das mittels der Klasse FlowLayout realisiert wird, ordnet alle Komponenten eines GUIs in der Reihenfolge horizontal im GUI an, in der die Komponenten mittels der add-Methode zugefügt werden. Ist in horizontaler Richtung kein Platz mehr verfügbar, so findet ein Wechsel derart statt, dass alle folgenden Komponenten in einer neuen horizontalen Abfolge angeordnet werden. Zur Definition eines derartigen Layouts wird stets die folgende Syntax verwendet:
FlowLayout fl = new FlowLayout(); Soll zusätzlich die Ausrichtung der Komponenten angegeben werden, so akzeptiert der Konstruktor FlowLayout auch einen der Parameter FlowLayout.LEFT, FlowLayout.RIGHT oder FlowLayout.CENTER, wodurch die Bestandteile des GUIs an der linken oder der rechten des jeweiligen Containers bzw. zentriert angeordnet werden. Nachdem eine Instanz eines Layouts erzeugt wurde, findet in der init-Methode eines Applets die Zuweisung des Layouts mittels der Methode setLayout(fl) statt, die als Parameter den Namen des Layouts (in diesem Fall fl) erwartet. Das folgende Beispiel erzeugt ein GUI für das Spiel „Schiffe versenken", das die Buttons „Spiel starten" und „Spiel beenden" enthält. Die Ausgabe des Beispiels ist in Abb. 4-29 angegeben.
import java.applet.*; FlowLayout fl= new FlowLayout(); setLayout(fl); } }
Abb. 4.29: Flow-Layout Zur Generierung eines Flow-Layouts kann auch ein Konstruktor verwendet werden, der neben dem bereits angegebenen Parameter zwei weitere enthält: Den horizontalen und den vertikalen Abstand in Pixeln zwischen den Komponenten eines GUIs. Sollen im obigen Beispiel die Abstände 20 Pixel (horizontal) bzw. 3 Pixel (vertikal) eingehalten werden, so müsste daher die folgende Zeile verwendet werden:
FlowLayout fl = new FlowLayout(FlowLayout.CENTER, 20, 3); Grid-Layout Der GridLayout-Manager erzeugt ein Raster aus Zeilen und Spalten, in denen die Komponenten angeordnet werden können. Die Reihenfolge, in der Komponenten diesem Gitter zugefügt werden, beginnt stets in der obersten Reihe mit dem am weitesten links stehenden Element. Anschließend werden Elemente solange in die erste Zeile eingefügt, bis der zur Verfügung stehende Platz erschöpft ist. Weitere Komponenten werden dann nach demselben Verfahren in der nächsten Zeile eingefügt. Zur Generierung eines Grid-Layouts wird die Klasse GridLayout mit folgenden Konstruktoren verwendet:
Das folgende
import java.applet.*; Button sb = new Button("Spiel starten"); setLayout(gl); } } class SpielCanvas extends Canvas { public void paint(Graphics screen) { int i; screen.drawLine(0, 0+i*20,200, 0+i*20); // Erzeuge vertikale Linien screen.drawLine(0+i*20, 0,0+i*20, 200); } } Mittels des Grid-Layouts können Elemente offensichtlich weit besser positioniert werden als mit dem Flow-Layout. Das erstere Layout wird daher nur dann verwendet, wenn ein GUI ein außerordentlich einfaches Erscheinungsbild haben soll.
Abb. 4.30: Grid-Layout Border-Layout Ein anderer Ansatz wird mit dem Border-Layout verfolgt. Hierbei kann bei jeder Komponente mittels einer der Himmelsrichtungen (Norden, Süden, Osten, Westen oder zentriert) angegeben werden, wo ein Element angeordnet werden soll. Zunächst wird in den verschiedenen Himmelsrichtungen der Platz belegt, den die dort angeordneten Komponenten benötigen. Der verbleibende Platz wird der Komponente im Zentrum zugewiesen. Zur Generierung eines Border-Layouts, das in der Klasse BorderLayout realisiert ist, stehen die folgenden Konstruktoren zur Verfügung:
Offensichtlich muss die Syntax der
add(String, <element>); Aus der in Abb. 4-31 dargestellten Bildschirmausgabe ist ersichtlich, dass auch dieses Layout eher bescheiden aussieht. Zudem wird nur eines der Spielfelder angezeigt. Offensichtlich ist eine weitere Gruppierung der Komponenten notwendig, die es bspw. ermöglicht, beide Spielfelder im Zentrum des GUIs anzuordnen.Hierzu werden in Java sog. Panels verwendet, die im Folgenden den bisher verwendeten Begriff des Containers ersetzen. Mittels Panels, deren Funktionsweise bereits erläutert wurde, sind Schachtelungen möglich, indem Komponenten gruppiert werden. Beispielsweise können beide Buttons des GUIs und beide Spielfelder in separaten Panels angeordnet werden, die anschließend in einem übergeordneten Panel gruppiert werden. Da für jedes Panel ein eigener Layout-Manager (auch verschiedene Layouts für verschiedene Panels) angegeben werden kann, sind erheblich komplexere Darstellungsformen möglich. Zur Anwendung eines Panels, das in der Klasse Panel implementiert ist, wird dieses zuerst angelegt, anschließend ein Layout definiert und dieses dem Panel zugewiesen:
Panel p = new Panel();
Abb. 4.31: Border-Layout
import java.applet.*; Button sb = new Button("Spiel starten"); //Spielfeld des Computers //Spielfeld des Benutzers setLayout(bl); } } class SpielCanvas extends Canvas { public void paint(Graphics screen) { screen.setColor(Color.black); // Erzeuge Quadrat als Spielfeldbegrenzung // Erzeuge horizontale Linien screen.drawLine(0, 0+i*20,200, 0+i*20); for (i = 1; i < 10; i++) screen.drawLine(0+i*20, 0,0+i*20, 200); } }
Abb. 4.32: Grid-Layout mit Panels
import java.applet.*; Button sb = new Button("Spiel starten"); //Spielfeld des Computers //Spielfeld des Benutzers SpielCanvas sc2 = new SpielCanvas(); p.setLayout(knoepfe); } } class SpielCanvas extends Canvas { public void paint(Graphics screen) { int i; screen.drawLine(0, 0+i*20,200, 0+i*20); // Erzeuge vertikale Linien screen.drawLine(0+i*20, 0,0+i*20, 200); } } Offensichtlich ist ein derartiges GUI bereits besser einsetzbar. Dennoch stehen weitaus umfassendere Mechanismen zur Verfügung, die in den nächsten beiden Abschnitten betrachtet werden. Es ist allerdings zu beachten, dass sich die bisher angesprochenen Schachtelungsmechanismen auf alle Layout-Strategien, also auch auf die im Folgenden beschriebenen Card- und GridBag-Layouts, beziehen. Card-Layout Ein Card-Layout definiert eine Gruppe von Containern oder Komponenten, von denen jeweils nur eine sichtbar ist. Jeder Container einer derartigen Gruppe wird als Card bezeichnet. Ein bekanntes Beispiel für die Anwendung dieses Layouts sind bspw. die Registerkarten, die in den Systemeigenschaften von Windows 98 der Firma Microsoft vorzufinden sind. Üblicherweise wird in einem Card-Layout ein Panel für jede Karte verwendet. Hierzu sind die folgenden Schritte zu durchlaufen:
Üblicherweise findet ein Kartenwechsel dann statt, wenn eine Benutzereingabe erfolgt ist. Das folgende Beispiel demonstriert die Anwendung des Card-Layouts, indem Felder zum Setzen von Optionen definiert werden. Die Umschaltung zwischen den Feldern kann allerdings an dieser Stelle noch nicht erläutert werden, da hierzu das Verständnis des in Kapitel 4.5 vorgestellten Event-Konzepts notwendig ist. Die Ausgabe des Beispiels ist in Abb. 4-33 dargestellt.
import java.applet.*; CardLayout c = new CardLayout(); c = new CardLayout(); } }
Abb. 4.33: Card-Layout GridBag-Layout Ein großer Nachteil der bisher erläuterten Layout-Strategien besteht darin, dass sie relativ unflexibel sind. Als Erweiterung des Grid-Layouts löst das GridBag-Layout einige dieser Probleme, indem eine Komponente mehr als eine Zelle des Gitters belegen kann, aber auch indem die Proportionen der Zeilen und Spalten des Gitters frei wählbar sind bzw. indem Komponenten innerhalb der Gitterzellen auf verschiedene Arten platziert werden können. Zur Verwendung dieses Layouts sind die folgenden Schritte zu durchlaufen:
Üblicherweise beginnt man den Entwurfsprozess eines GridBag-Layouts auf Papier. Hierbei ist zu beachten, dass jede Komponente in einer eigenen Gitterzelle untergebracht werden muss. Eine Komponente kann sich allerdings durchaus auch über mehrere Gitterzellen erstrecken (horizontal und vertikal). Es ist in diesem Zusammenhang hilfreich, die Komponenten mit Koordinaten zu versehen. Hierbei sollten keine Pixel- sondern Gitterkoordinaten verwendet werden. Das Element, das links oben im Gitter angebracht wäre, würde daher mit (0,0) bezeichnet werden, das Element rechts davon mit (0,1). Im nächsten Schritt setzt man die Eigenschaften der verschiedenen Elemente. Hierzu ist die Verwendung einer Hilfsmethode sinnvoll, da stets dieselbe Menge von Eigenschaften für jedes Element zu setzen ist. Die Eigenschaften, die gesetzt werden können, sind in Tab. 4-3 angegeben. |
|
|
Tab. 4.3: Eigenschaften innerhalb eines GridBagConstraints-Objekts Eine Hilfsroutine, die die Werte geeignet setzt, ist im Folgenden angegeben.
void setzeGridBagConstraints(GridBagConstraints gbc, int gx, int gy, int gw, int gh, int f, int ix, int iy, Insets in, int a, float wx, float wy) { gbc.gridx = gx; } Im Folgenden soll die Benutzeroberfläche des Spiels „Schiffe versenken" ansprechend gestaltet werden. Zur besseren Erläuterung werden die Code-Segmente durch Erklärungen unterbrochen. Setzt man allerdings die Segmente wieder zusammen, so erhält man das vollständige Java-Applet des GUIs. Zuerst wird der Kopf folgendermaßen definiert:
import java.applet.*; Anschließend wird die Hilfsroutine definiert, die die Eigenschaften der Komponenten setzt:
void setzeGridBagConstraints(GridBagConstraints gbc, int gx, int gy, int gw, int gh, int f, int ix, int iy, Insets in, int a, float wx, float wy) {gbc.gridx = gx; } Im nächsten Schritt wird die Methode getInsets überschrieben, die einen 10 Pixel breiten Rand um die Komponenten anlegt.
public Insets getInsets() { return new Insets(10,10,10,10); } In der folgenden init-Methode wird zuerst ein Panel angelegt, dem ein Grid-Layout zugewiesen wird. Dieses Panel enthält die im Folgenden definierten Buttons. Weiterhin werden zu Beginn der Methode das GridBagLayout und die GridBagConstraints definiert.
public void init () { GridLayout g = new GridLayout(4,1); // Anlegen der Buttons Im nun folgenden Teil werden die Eigenschaften des Panels mit Hilfe der Methode setzeGridBagConstraints gesetzt. Das Panel befindet sich in Zelle (0,0) und hat eine horizontale Ausdehnung von 33% des GUI-Bereichs bzw. eine horizontale von 50%. Anschließend werden die zwei Spielfelder definiert. Der hierzu notwendige Code wurde bereits im Zusammenhang mit Grid-Layouts definiert. Neu ist hierbei allerdings, dass beide Felder mit Labels bezeichnet werden. Bei jedem der Objekte erfolgt ein Aufruf der Methode setzeGridBagConstraints.
setzeGridBagConstraints(gbc,0,0,1,1,GridBagConstraints.HORIZ ONTAL, 0,0,getInsets(), GridBagConstraints.NORTH, 0.33f, 0.5f); add(p); //Spielfeld des Computers //Spielfeld des Benutzers } } Mit Abschluss dieser Klassendefinition folgt noch die Definition der Klasse, die für die Erzeugung der Spielfelder notwendig ist. Diese wurde bereits erläutert. Die Ausgabe der Oberfläche ist in Abb. 4-34 angegeben.
class SpielCanvas extends Canvas { public void paint(Graphics screen) { int i; screen.drawLine(0, 0+i*20,200, 0+i*20); // Erzeuge vertikale Linien screen.drawLine(0+i*20, 0,0+i*20, 200); } }
Abb. 4.34: Oberfläche des Spiels „Schiffe versenken" |
|
|