Entwicklung von E-Learning Komponenten zur...

153
F Entwicklung von E-Learning Komponenten zur Computergrafik Development of E-learning components for computer graphics Markus Stollenwerk, Rainer Friesen und Daniel Valentin Master Projektstudium Betreuer: Prof. Dr. Ing. Fritz Nikolai Rudolph Trier, 28.02.2007

Transcript of Entwicklung von E-Learning Komponenten zur...

F

Entwicklung vonE-Learning Komponenten zurComputergrafik

Development of E-learning components for computer graphics

Markus Stollenwerk, Rainer Friesen und Daniel Valentin

Master Projektstudium

Betreuer: Prof. Dr. Ing. Fritz Nikolai Rudolph

Trier, 28.02.2007

Kurzfassung

Ziel dieses Tutorials ist es die grundlegenden Techniken der Computergrafik zuerklaren. Dazu werden zunachst die Grundlagen der Programmiersprachen Javaund C# erklart. Diese Grundlagen befassen sich mit dem Erstellen von grafischenBenutzeroberflachen und der Verwendung von diesen.Ein weiterer Bereich beschaftigt sich mit den verschiedenen Arten von Software-patterns bzw. Softwareentwurfsmustern. Hier werden exemplarisch die wichtigstenEntwurfsmuster erlautert.Im letzten Abschnitt wird die Verwendung der Java2D Umgebung erklart. Diesbeinhaltet die grundliegende Moglichkeit zu Zeichnen und diese Zeichnungen zumanipulieren. Des Weiteren wird auf die Verwendung von Schriften und Transpa-renz eingegangen.

Inhaltsverzeichnis

1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

2 Grundlagen Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32.1 Fenster und grafische Oberflachen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2.1.1 Erstellen eines JFrames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32.1.2 Hinzufugen von grafischen Elementen . . . . . . . . . . . . . . . . . . . . . . 62.1.3 Anordnen von Objekten mit Layouts . . . . . . . . . . . . . . . . . . . . . . 82.1.4 Verwenden von Listenern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282.1.5 Erstellen von Menuleisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

2.2 Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422.3 GUI Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

2.3.1 Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492.3.2 Textelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522.3.3 Slider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532.3.4 ComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532.3.5 Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562.3.6 Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562.3.7 Baume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602.3.8 weitere GUI Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

3 Grundlagen C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633.1 Erstellung eines Projekts in Visual Studio . . . . . . . . . . . . . . . . . . . . . . . 63

3.1.1 Verweise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643.2 Fenster und GUI-Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

3.2.1 Beispiel 1 - ein einfaches Fenster in C# . . . . . . . . . . . . . . . . . . . . 653.2.2 Beispiel 2 - Buttons und EventHandler . . . . . . . . . . . . . . . . . . . . 673.2.3 Beispiel 3 - Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693.2.4 Beispiel 4 - Maus-Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713.2.5 Beispiel 5 - Web-Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733.2.6 Beispiel 6 - Menu- und Statusbars . . . . . . . . . . . . . . . . . . . . . . . . . 753.2.7 Beispiel 7 - ComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803.2.8 Beispiel 8 - MDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843.2.9 Beispiel 9 - Radiobuttons und Checkboxen . . . . . . . . . . . . . . . . . 85

Inhaltsverzeichnis IV

3.2.10Beispiel 10 - TreeView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

4 Grundlagen Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 914.1 Observer Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 914.2 MVC Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964.3 Singleton Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1014.4 State Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034.5 Factory Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

5 Grundlagen Java 2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105.1 Zeichnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

5.1.1 Zeichnen vor Java2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105.1.2 Zeichnen mit Java2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

5.2 Translation, Rotation und Skalierung . . . . . . . . . . . . . . . . . . . . . . . . . . . 1175.3 Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1215.4 Transparenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

6 2D-Grafik in C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1266.1 Einfuhrung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1266.2 2D-Grafik in C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

6.2.1 Initialisierung des Grafikkontexts . . . . . . . . . . . . . . . . . . . . . . . . . 1266.2.2 Beispiel 1 - Die erste 2D-Grafik . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286.2.3 Beispiel 2 - Ellipsen und Rechtecke . . . . . . . . . . . . . . . . . . . . . . . . 1306.2.4 Beispiel 3 - Brushes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1316.2.5 Beispiel 4 - Polygone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1336.2.6 Beispiel 5 - GraphicsPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1356.2.7 Beispiel 6 - Transformationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1366.2.8 Einfuhrung Splines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1386.2.9 Splines 1 - Hermite-Splines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1386.2.10Splines 2 - Bezier-Splines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1406.2.11Splines 3 - Cardinal Splines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

6.3 Fonts in C#. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1456.4 Bitmaps in C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

Abbildungsverzeichnis

2.1 JFrames stellen die Fenster in Java dar . . . . . . . . . . . . . . . . . . . . . . . . . 32.2 JFrame, bei welchem die Große mit pack automatisch angepasst

wird. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.3 Der zweite JButton uberschreibt den ersten JButton im JFrame. . . . 92.4 Ein FlowLayout ordnet die JLabels nebeneinander an. . . . . . . . . . . . . 112.5 Ein GridLayout ordnet die JLabels wie in einem Gitter an. . . . . . . . . 112.6 Ein BorderLayout ordnet die JPanels so an, dass ein Zentrum und

ein Rand entstehen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.7 Verwendung eines BoxLayouts zur Erstellung eines Login-Bildschirms. 182.8 Mit einem GridBagLayout kann man komplexere grafische

Oberflachen erstellen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232.9 Das CardLayout ist das einzige Layout in Java, welches

Interaktionen des Benutzers zulasst. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272.10 Eine Menuleiste, wie sie in fast jedem Programm vorkommt. . . . . . . 392.11 Hello World Applet in einem Webbrowser . . . . . . . . . . . . . . . . . . . . . . . 432.12 Anzeige der verschiedenen Meilensteine . . . . . . . . . . . . . . . . . . . . . . . . . 452.13 Ansicht eines Frames aus einem JApplet . . . . . . . . . . . . . . . . . . . . . . . . 482.14 Ansicht der verschiedenen Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502.15 SicherePassworteingabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522.16 Slider und ComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542.17 Menus und Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572.18 Ein Baum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

3.1 Neues Projekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643.2 Verweise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653.3 Beispielprogramm frames01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663.4 Anwendung Frames02 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683.5 Anwendung Frames03 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703.6 Anwendung Frames04 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723.7 Anwendung Frames05 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753.8 Menuaufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763.9 Anwendung Frames07 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833.10 Anwendung Frames08 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

Abbildungsverzeichnis VI

3.11 Anwendung Frames09 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873.12 TreeView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873.13 Anwendung Frames10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

4.1 Observer-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924.2 Model-View-Control-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964.3 Singleton-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1014.4 State-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034.5 Automatenmodell eines Toroffners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

5.1 Ausgabe des Programms Draw01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105.2 Ausgabe des Programms Draw02 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1145.3 Transition des Rechtecks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1205.4 Ausgabe des Programms Font01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1225.5 Ausgabe des Programms Transparency01 . . . . . . . . . . . . . . . . . . . . . . . 124

6.1 Neue Windows Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1276.2 Beispielprogramm 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1296.3 Beispielprogramm 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1316.4 Beispielprogramm 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1336.5 Beispiel 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1346.6 GraphicsPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1356.7 Rotation eines GraphicsPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1376.8 Bezier-Kurve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1386.9 Hermite-Kurve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1406.10 Bezierkurve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1426.11 Auswirkung des Tensionwerts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1426.12 Cardinal Splines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1446.13 Fonts in C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1456.14 Bitmaps in C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

1

Einleitung

Ziel dieser Ausarbeitung ist es, einen Einblick in die Verwendung der Computer-grafik zu geben. In der heutigen Zeit gibt es immer mehr Anwendungsgebiete furdie Computergrafik. Angefangen bei der computerunterstutzten Designarbeit uberdie Spiele-Industrie bis hin zu der Entwicklung von Grafiken fur Bilder und Filme.Bei all diesen Aufgaben werden die Grundlagen der Computergrafik benotigt. Umzu diesen komplexen Themengebieten einen Einstieg zu finden werden zunachstdie grundlegenden Funktionen der Programmiersprachen Java und C# erlautert.

Im ersten Kapitel”Grundlagen Java“ werden die ersten Schritte bei der Pro-

grammierung einer grafischen Benutzeroberflache mit Java beschrieben. Dazugehort als erstes das Erstellen eines Fensters mit Hilfe eines JFrames, das Hin-zufugen von grafischen Elementen und Anordnen mit Hilfe von LayoutManagern,sowie die Verwendung von Listenern und Menulistenern. Der zweite Schritt beimUmgang mit Java ist die Verwendung von Applets. Dazu gehoren der allgemeineAufbau eines Applets und die Einbindung in eine Webseite. Der dritte Abschnittbefasst sich mit den verschiedenen, in Java verfugbaren, GUI-Elementen. Hier wer-den die Funktionsweise und die Verwendung beschrieben. Bei den GUI-Elementenhandelt es sich um die verschiedenen Arten von Buttons, Textbausteinen und an-deren grafischen Ein- und Ausgabemoglichkeiten.

Das zweite Kapitel”Grundlagen C#“ gibt einen Einblick in den Umgang mit

der Programmiersprache C#. Dazu wird zunachst erklart wie mit Hilfe der Ent-wicklungsumgebung Visual Studio neue Projekte erstellt werden konnen. Daranschließt sich eine Einfuhrung in die ersten Schritte beim Umgang mit C# an. Alsletzter Abschnitt in diesem Kapitel werden die verschiedenen Fenster und GUI-Elemente erklart. Dazu zahlen neben den Standardelementen, wie Buttons undLabels, auch Besonderheiten wie der Webbrowser. Zusatzlich wird hier der Um-gang mit Mausevents sowie Menus und Statusanzeigen erlautert.

Das nachste Kapitel”Grundlagen Patterns“ beschaftigt sich mit den wichtigs-

ten Entwurfsmustern in der Softwareentwicklung. Dazu wird der Aufbau und dieVerwendung der Entwurfsmuster Observer, Model-View-Control, Singleton, State

1 Einleitung 2

sowie Factory beschrieben.

Im letzten Kapitel”Grundlagen Java2D“wird der Umgang mit der Java2D Um-

gebung beschrieben. Dazu wird in einem ersten Schritt ein Vergleich zwischen demZeichnen vor und nach Java2D gezogen. Im weiteren Verlauf werden die Moglich-keiten bei der Grafikprogrammierung mit Java2D aufgezeigt. Dazu gehort als einerder wichtigsten Punkte die Moglichkeit der Bildmanipulation durch Translation,Rotation und Skalierung. Als Abschluss wird dann noch auf die Verwendung vonSchriften eingegangen und das Konzept der Transparenz erlautert.

2

Grundlagen Java

2.1 Fenster und grafische Oberflachen

Im folgenden Abschnitt wird gezeigt, wie man mit Java eigene Fenster erstellt undkomplette grafische Benutzeroberflachen aufbaut. Dazu gehort die Erzeugung vonButtons, Listen etc. und das Einfugen dieser Objekte in ein Fenster, sowie dieVerwendung von Listenern, zum Abfangen von Benutzereingaben.

Dazu werden die Java-Packages javax.swing und java.awt verwendet. Es ist alsoin jedem Beispiel notig diese Packages oder zumindest Teile davon zu importieren,da diese die wichtigsten Elemente einer grafischen Oberflache zur Verfugung stel-len.Um weitere Informationen und Hilfe zu erhalten, ist es sehr hilfreich einen Blickin das Java Swing Tutorial [SUNb] zu werfen.

2.1.1 Erstellen eines JFrames

In der Abbildung 2.1 sieht man ein JFrame, aus dem Package javax.swing. DiesesJFrame stellt ein Fenster dar, wie es ublicherweise in den heutigen Betriebssyste-men verwendet wird um grafische Oberflachen zu erzeugen.

Abbildung 2.1. JFrames stellen die Fenster in Java dar

2.1 Fenster und grafische Oberflachen 4

Ein solches JFrame zu Erstellen ist ziemlich simpel. Man erstellt einfach einObjekt der Klasse JFrame und fuhrt die passenden Methoden zum Konfigurierendes JFrames aus.

Zuerst sollte man eine Klasse mit beliebigem Namen und einer Main-Methodeerstellen. Zusatzlich muss man noch die Klasse JFrame importieren, welche sich indem Package javax.swing befindet.

Als nachstes sollte man dann das JFrame-Objekt erstellen und konfigurieren.Dies geschieht mit dem folgenden Quellcode.

public stat ic void main ( St r ing [ ] a rgs ){

JFrame frm = new JFrame ( ) ;frm . s e tT i t l e ( ”JFrame mit s e t S i z e ( ) ” ) ;

frm . se tDe fau l tC loseOperat i on (JFrame .EXIT ON CLOSE) ;

frm . s e t S i z e ( 300 , 200 ) ;frm . s e tLoca t i on ( 5 0 , 5 0 ) ;frm . s e tV i s i b l e ( true ) ;

}

Hier wird ein JFrame-Objekt erstellt und danach dessen Titel eingestellt. DerTitel ist der Text der in der oberen Fensterleiste erscheint.Der Methode setDefaultCloseOperation wird dann noch die Konstante JFra-me.EXIT ON CLOSE ubergeben. Diese sorgt dafur, dass das komplette Programmbeendet wird, wenn man das Fenster schließt. Es gibt unterschiedliche Konstan-ten, welche verwendet werden konnen um verschiedene Effekte beim Schließen einesFensters zu erzeugen.

• JFrame.DO NOTHING ON CLOSEBeim Schließen passiert einfach nichts.

• JFrame.EXIT ON CLOSEBeendet das Programm, wenn man dieses Fenster schließt.

• JFrame.HIDE ON CLOSE (Standard bei JFrame und JDialog)Versteckt das Fenster anstatt es zu schließen.

• JFrame.DISPOSE ON CLOSE (Standard bei JInternalFrame)Verwirft das Fenster komplett und entfernt es aus dem Speicher.

Nachdem nun die defaultCloseOperation festgelegt wurde stellen wir noch dieGroße und die Position des Fensters mit den Methoden setSize() und setLocati-on() ein. Der Methode setSize() kann man entweder zwei int-Werte fur Breite undHohe ubergeben oder ein Dimension-Objekt. Die Methode setLocation() erhalt alsParameter zwei int-Werte oder ein Objekt der Klasse Point.

2.1 Fenster und grafische Oberflachen 5

Zu guter Letzt wird das Fenster dann noch angezeigt. Dies geschieht mit dem Auf-ruf setVisible(true).

Der Beispielquellcode fur dieses Beispiel kann auch noch einmal in der DateiFrameCreator01.java gefunden werden. Wenn er kompiliert und gestartet wird,wird das Fenster aus der Abbildung 2.1 erzeugt.

Man sieht also, dass man mit minimalem Aufwand ein Fenster erstellen kann. Indiesem Fall wurde jedoch nur ein sehr einfaches JFrame erstellt und dieses mit dendazugehorigen Methoden verandert. Ublicherweise erzeugt man jedoch eine eigeneFensterklasse, von welcher man dann auch mehrere Instanzen erstellen kann.

Dazu schreibt man eine neue Klasse, welche von der Klasse JFrame erbt undpasst dann in dieser Klasse, beispielsweise im Konstruktor, die jeweiligen Einstel-lungen an. Das ganze sieht dann folgendermaßen aus.

import javax . swing . JFrame ;

/∗∗ Er s t e l l e n e ine s Fens ters mit e iner∗ automatischen Groesse∗/

class MyFrame extends JFrame{public MyFrame(){

//Einen T i t e l e i n s t e l l e nsuper ( ”JFrame mit pack ( ) ” ) ;

/∗∗ Das JFrame so e i n s t e l l e n , dass das∗ Programm beim Sch l i eßen des JFrames∗ beendet wird∗/

s e tDe fau l tC loseOperat i on (JFrame .EXIT ON CLOSE) ;

//Groesse wird automatisch angepass tpack ( ) ;

//Die Pos i t i on e i n s t e l l e ns e tLocat i on ( 5 0 , 5 0 ) ;

//Das JFrame anze igens e tV i s i b l e ( true ) ;

}

2.1 Fenster und grafische Oberflachen 6

}

public class FrameCreator02{public stat ic void main ( St r ing [ ] a rgs ){

new MyFrame ( ) ;}}

In diesem Beispiel wird die neue Klasse MyFrame erstellt, welche von JFra-me erbt. In ihrem Konstruktor wird der Superkonstruktor, also der Konstruktorder Klasse JFrame, aufgerufen um den Titel einzustellen. Danach werden, wie imvorherigen Beispiel, die Einstellungen fur das JFrame vorgenommen, indem dievererbten Methoden verwendet werden.Eine kleine Anderung wurde jedoch noch zusatzlich vorgenommen. Statt der Me-thode setSize() verwenden wir, in diesem Beispiel die parameterlose Methodepack(), welche eine Methode der Oberklasse Window (java.awt.Window) ist, vonwelcher JFrame erbt. Die Methode pack() passt die Große des Fensters automa-tisch so an, dass alle Elemente des JFrames hineinpassen aber trotz allem dasFenster moglichst klein ist. In unserem Beispiel (siehe Abb.2.2), sieht man nur dieTitelleiste, da sich kein anderes Element auf dem Fenster befindet.

Abbildung 2.2. JFrame, bei welchem die Große mit pack automatisch angepasstwird.

Der Quellcode zu diesem Beispiel befindet sich in der Datei FrameCreator02.javaund kann ebenfalls kompiliert und ausgefuhrt werden.

2.1.2 Hinzufugen von grafischen Elementen

Im vorherigen Abschnitt wurde nun erlautert, wie man ein eigenes Fenster erstellt,in der Große verandert und anzeigt. In der folgenden Passage wird nun gezeigtwie man ein grafisches Element zu diesem Fenster hinzufugt. Welche verschiede-nen grafischen Elemente es in Java gibt wird in einem spateren Abschnitt nahererlautert (Abschnitt 2.3).

Jedes JFrame in Java besitzt ein so genanntes ContentPane. Das ContentPaneist ein Container, in welchen man verschiedene grafische Elemente einfugen kann.Zu diesen Elementen gehoren Buttons, Textfelder, sowie andere Container, welchewiederum Elemente enthalten konnen.

Um nun auf das ContentPane zugreifen zu konnen, kann man ganz einfachdie Methode getContentPane, des JFrames verwenden. Das passende ContentPa-

2.1 Fenster und grafische Oberflachen 7

ne kann dann dazu verwendet werden um Elemente aufzunehmen, welche auf demJFrame angezeigt werden sollen. Das Hinzufugen eines Elements wird durch dieMethode add(Component comp) ausgefuhrt.

Der folgende Quellcode zeigt wie man ein Element erstellt, welches man hin-zufugen kann und wie man es dann auch dem ContentPane ubergibt. In diesemBeispiel handelt es sich bei dem Objekt um einen JButton, einen Knopf, auf wel-chen man klicken kann.

import java . awt . event . ActionEvent ;import java . awt . event . Act i onL i s t ene r ;import javax . swing . JButton ;import javax . swing . JFrame ;import javax . swing . JOptionPane ;

/∗∗ Hinzuf ugen e ine s Buttons in e in JFrame∗ und verwenden e ine s L i s t ene r s∗/

class MyButtonFrame extends JFrame implements Act ionL i s t ene r {public MyButtonFrame (){

super ( ”JFrame mit Button” ) ;s e tDe fau l tC loseOperat i on (JFrame .EXIT ON CLOSE) ;

/∗∗ Er s t e l l e n e ine s JButtons∗ mit e iner Beschr i f t ung∗/

JButton btn = new JButton ( ”Druck mich” ) ;btn . addAct ionLis tener ( this ) ;

/∗∗ Einf ugen des Buttons auf∗ das ContentPane des JFrames∗/

getContentPane ( ) . add ( btn ) ;

pack ( ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}

2.1 Fenster und grafische Oberflachen 8

public void act ionPerformed ( ActionEvent evt ){JOptionPane . showMessageDialog (

null ,”Der Button f u n k t i o n i e r t ! ” ,”Button f u n k t i o n i e r t ” ,JOptionPane .INFORMATION MESSAGE) ;

}}

public class FrameCreator03 {public stat ic void main ( St r ing [ ] a rgs ){

new MyButtonFrame ( ) ;}}

Zuerst wird wieder der Standardkonstruktor von JFrame aufgerufen, um denTitel einzustellen und es wird die DefaultCloseOperation eingerichtet (wie in Ab-schnitt 2.1.1 beschrieben).Daraufhin wird ein neuer JButton erstellt und diesem wird ein ActionListener, wel-cher in der gleichen Klasse implementiert wurde, ubergeben. Nahere Informationenzu Listenern werden im Abschnitt 2.1.4 dargestellt. Durch diesen ActionListenerwird die Methode actionPerformed(ActionEvent evt) ausgefuhrt, sobald der Be-nutzer auf den Knopf druckt und es wird ein JOptionPane angezeigt, welches eineMeldung ausgibt. Nachdem nun der JButton erstellt wurde, wird er noch zu demFenster hinzugefugt. Um dies durchzufuhren nimmt sich das Programm, mit derMethode getContentPane(), das Container-Objekt des JFrames und fugt mit derMethode add(btn) den JButton zu dem Fenster hinzu.Danach wird wie bereits erklart, dass Fenster in der Große angepasst und ange-zeigt.

2.1.3 Anordnen von Objekten mit Layouts

Wenn man nicht nur ein einzelnes grafisches Element, sondern gleich mehrere zueinem JFrame hinzufugen mochte, muss man so genannte Layouts verwenden. Lay-outs sind Strukturen, welche grafische Elemente in einem JFrame oder einer an-deren Containerklasse so anordnen, wie man es gerne mochte.

Ohne diese Layouts ist es beispielsweise nicht moglich, Buttons o.a. neben-einander anzuordnen oder sie in einer anderen Anordnung auf einem JFrame zuverteilen. Wurde man beispielsweise den folgenden Quellcode in einem Programmeinfugen, wurde nur der zuletzt hinzugefugte Button angezeigt werden. Das Pro-gramm wurde zuerst den ersten Button einfugen und der zweite wurde diesen dannuberzeichnen (Abb. 2.3).

2.1 Fenster und grafische Oberflachen 9

// E r s t e l l e n der JButtons mit e iner Beschr i f t ungJButton btn = new JButton ( ”Druck mich” ) ;btn . addAct ionLis tener ( this ) ;

JButton btn2 = new JButton ( ”Button 2” ) ;btn2 . addAct ionLis tener ( this ) ;

//Einf ugen der Buttons auf das ContentPane des JFramesgetContentPane ( ) . add ( btn ) ;getContentPane ( ) . add ( btn2 ) ;

Abbildung 2.3. Der zweite JButton uberschreibt den ersten JButton im JFrame.

Um eine moglichst große Vielfalt an verschiedenen grafischen Oberflachen zuermoglichen gibt es unterschiedliche Arten von Layouts, welche man verwendenund auch kombinieren kann. Die einzelnen Layouts werden in den folgenden Ab-schnitten naher erlautert und es wird gezeigt, welche Art von Oberflachen manmit welchem Layout erstellen kann. Die Quellcodes der Beispielprogramme, in denfolgenden Layoutabschnitten sind in den Dateien FrameCreator4.java bis Frame-Creator9.java enthalten.

FlowLayout

Das erste Layout, welches hier vorgestellt wird, ist das so genannte FlowLayout.Dieses ermoglicht Objekte nebeneinander zu platzieren. Um das FlowLayout zuverwenden kann man genauso vorgehen, wie in den Kapiteln zuvor beschrieben.Der einzige Unterschied besteht darin, dass man dem Container des JFrames einFlowLayout zuweist. Dies geschieht uber die Methode setLayout(Layout) des je-weiligen Containers. Im folgenden Quellcode sieht man eine passende Anwendung,welche zeigt wie man ein Layout einstellt und danach mehrere JLabels zu demContainer hinzufugt. JLabels sind Objekte, welche einen einfachen Text anzeigenkonnen.

2.1 Fenster und grafische Oberflachen 10

import java . awt . FlowLayout ;import javax . swing . JFrame ;import javax . swing . JLabel ;

/∗∗ Hinzuf ugen mehrerer Labe l s mit H i l f e e ine s FlowLayouts∗/

class FlowLayoutFrame extends JFrame{public FlowLayoutFrame (){

super ( ”FlowLayout” ) ;s e tDe fau l tC loseOperat i on (JFrame .EXIT ON CLOSE) ;

// E i n s t e l l e n e ine s FlowLayoutsgetContentPane ( ) . setLayout (new FlowLayout ( ) ) ;

//Hinzuf ugen von e in i g en JLabe l sJLabel l b l ;for ( int i =0; i <5; i++){

l b l = new JLabel ( ” JLabel ” + ( i +1)) ;getContentPane ( ) . add ( l b l ) ;

}

pack ( ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}}

public class FrameCreator04 {public stat ic void main ( St r ing [ ] a rgs ){

new FlowLayoutFrame ( ) ;}}

Der gezeigte Quellcode unterscheidet sich nur in wenigen Punkten von demQuellcode aus dem Abschnitt 2.1.2. Zum Einen werden hier keine JButtons, son-dern JLabels hinzugefugt und zum Anderen wird hier, wie oben beschrieben, einLayout mit new FlowLayout() erstellt und dieses dem Container des JFrames uber-geben. Dieses Layout regelt dann selbststandig die Anordnung der einzelnen, mitder For-Schleife erstellten, JLabels. Ein passendes Bild, welches solch ein Flow-Layout zeigt, ist die Abbildung 2.4.

2.1 Fenster und grafische Oberflachen 11

Abbildung 2.4. Ein FlowLayout ordnet die JLabels nebeneinander an.

GridLayout

Das GridLayout ist dem FlowLayout sehr ahnlich. Der eigentliche Unterschiedbesteht darin, dass man sich die Anordnung nicht als Linie vorstellen sollte, wiebei dem FlowLayout, sondern als Gitter. Daher kommt auch der Name des Grid-Layouts. In dem GridLayout konnen alle Objekte in einer Art Raster oder Gitterangeordnet werden, wobei man die Anzahl der Spalten und Zeilen angeben kann.Dabei muss man nur eine sehr kleine Anderung am Quellcode des FlowLayouts,aus dem Abschnitt 2.1.3, vornehmen. Es wird einfach statt dem FlowLayout einGridLayout an die Methode setLayout(Layout) ubergeben. Des Weiteren kannuber den Konstruktor des GridLayouts angegeben werden, wieviele Spalten undZeilen das Raster haben soll. Der erste Wert gibt die Anzahl der Zeilen an und derzweite Wert logischerweise die Anzahl der Spalten. Ubergibt man den Wert 0 anden Konstruktor werden endlos viele Spalten bzw. Zeilen verwendet. Der Aufrufder setLayout(Layout) Methode sieht dann folgendermaßen aus.

getContentPane ( ) . setLayout (new GridLayout ( 2 , 3 ) ) ;

Ein Screenshot von solch einem GridLayout Beispielprogramm zeigt die Abbil-dung 2.5.

Abbildung 2.5. Ein GridLayout ordnet die JLabels wie in einem Gitter an.

BorderLayout

Das BorderLayout ist etwas komplexer als die bisher vorgestellten Layouts. Es wirdnormalerweise nicht dazu verwendet JButtons oder JLabels anzuordnen. Stattdes-sen fugt man in einen Container, welcher ein BorderLayout verwendet, normaler-weise JPanels ein. Ein JPanel ist wiederum ein Container, ahnlich wie das Con-tentPane eines JFrames. In solch ein JPanel kann man also wiederum JButtonsoder JLabels etc. einfugen.

Im folgenden Beispielprogramm sieht man wie man ein BorderLayout dazu ver-wendet um JPanels anzuordnen. Dabei besteht ein BorderLayout immer aus einemZentrum und vier Randern. Der Quellcode zeigt wie man einzelne JPanels, in den

2.1 Fenster und grafische Oberflachen 12

einzelnen Bereichen anordnet. Dabei wird der zweite Ubergabewert der add()-Funktion eines Containers verwendet. An diesen wird eine Konstante ubergeben,welche in der Klasse BorderLayout definiert ist und angibt, wo sich ein grafischesElemente befindet. Es gibt die Konstanten CENTER, NORTH, EAST, SOUTHund WEST. Dabei bedeutet beispielsweise die Konstante NORTH, dass das jewei-lige Objekt am nordlichen Rand eingefugt werden soll.

import java . awt . BorderLayout ;import java . awt . Color ;import javax . swing . JFrame ;import javax . swing . JLabel ;import javax . swing . JPanel ;

/∗∗ Hinzuf ugen von JPanels und JLabe l s∗ und verwenden e ine s BorderLayout∗/

class BorderLayoutFrame extends JFrame{public BorderLayoutFrame (){

super ( ”BorderLayout” ) ;s e tDe fau l tC loseOperat i on (

JFrame .EXIT ON CLOSE) ;

// E i n s t e l l e n e ine s BorderLayoutsgetContentPane ( ) . setLayout (new BorderLayout ( ) ) ;

Color [ ] c o l o r s = {Color .RED,Color .BLUE,Color .YELLOW,Color .GREEN,Color .ORANGE} ;

S t r ing [ ] s t r i n g s = {”Center ” ,”Norden” ,”Osten” ,”Suden” ,”Westen” } ;

S t r ing [ ] p o s i t i o n s = {BorderLayout .CENTER,BorderLayout .NORTH,BorderLayout .EAST,

2.1 Fenster und grafische Oberflachen 13

BorderLayout .SOUTH,BorderLayout .WEST} ;

// E r s t e l l e n der JPanels und der JLabe l sJPanel panel ;JLabel l b l ;for ( int i =0; i <5; i++){

panel = new JPanel ( ) ;panel . setBackground ( c o l o r s [ i ] ) ;

l b l = new JLabel ( s t r i n g s [ i ] ) ;panel . add ( l b l ) ;

getContentPane ( ) . add (panel , p o s i t i o n s [ i ] ) ;

}

s e t S i z e ( 400 , 300 ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}}public class FrameCreator06 {public stat ic void main ( St r ing [ ] a rgs ){

new BorderLayoutFrame ( ) ;}}

In diesem Beispiel werden die Hintergrundfarben der JPanels in verschiedenenFarben angezeigt, um deutlich zu machen, wo sich welches JPanel befindet. Dazuwird ein Array colors erstellt, welches verschiedene Farben enthalt. Des Weiterenwird ein Array positions mit Strings erstellt, welche die Positionen der JPanelsbeinhalten. Danach werden dann mit einer For-Schleife neue JPanels erstellt unddiese in der passenden Farbe und an der passenden Position angezeigt. Hier siehtman gut, dass die Methode add(), in diesem Beispiel, zwei Argumente ubergebenbekommt. Das erste ist das hinzuzufugende Objekt und das zweite die Positiondes Objekts in dem Container. Naturlich darf man auch hier nicht vergessen vor-her das Layout einzustellen. Dies geht hier genauso wie bei dem FlowLayout oderdem GridLayout, indem man eine Instanz von BorderLayout an die Methode set-Layout(Layout) ubergibt. Auch hier gibt es wieder eine Beispielabbildung (Abb.2.6).

2.1 Fenster und grafische Oberflachen 14

Abbildung 2.6. Ein BorderLayout ordnet die JPanels so an, dass ein Zentrumund ein Rand entstehen.

BoxLayout

Ein so genanntes BoxLayout bietet dem Programmierer schon wesentlich mehrGestaltungsmoglichkeiten, als alle bisher vorgestellten Layouts und lasst sich trotzdieser Moglichkeiten relativ einfach verwenden.

Die grobe Ausrichtung aller Objekte in einem BoxLayout wird uber Konstantenfestgelegt. Dabei orientieren sich alle Objekte in einem Container, welcher einBoxLayout verwendet, an einer horizontalen oder einer vertikalen Linie. Es gibtvier verschiedene Konstanten, welche verwendet werden konnen:

• X AXIS - Hier werden die Objekte von links nach rechts angeordnet• Y AXIS - Hier werden die Objekte von oben nach unten angeordnet• LINE AXIS - Diese Konstante existiert erst seit Java 1.4 und ordnet die Objekte

von links nach rechts oder von rechts nach links an, je nachdem, welche Spracheim System eingestellt ist. Dabei haben bestimmte Sprachen eine umgekehrteOrientierung, im Vergleich mit der Konstanten X AXIS.

• PAGE AXIS - Diese Konstante existiert ebenfalls erst seit Java 1.4 und ordnetdie Objekte von oben nach unten oder von unten nach oben an. Genau wie beiLINE AXIS gibt es hier eine sprachenabhangige Orientierung.

Des Weiteren bietet ein BoxLayout die Moglichkeit leere Bereiche in einen Con-tainer einzufugen und somit die Objekte einfach anzuordnen. Es gibt hier dreiverschiedene Arten von Objekten:

• RigidArea - Eine RigidArea bekommt eine Breite und Hohe ubergeben, welchesie ausfullen soll. Diese wird beispielsweise verwendet, wenn man zwischen zweiObjekten einen bestimmten Abstand einhalten mochte.

2.1 Fenster und grafische Oberflachen 15

• Glue - Bei einem Glue kann es sich um einen horizontalGlue oder um einenverticalGlue handeln. Diese fullen soviel Platz aus, wie sie bekommen konnen.

• Box.Filler - Ein Filler kann eine minimale, maximale und eine gewunschte Großebesitzen. So kann man beispielsweise ein Objekt erstellen, welches mindestens5 Pixel und hochstens 50 Pixel breit sein sollte.

Im folgenden Quellcode sieht man ein kleines Beispielprogramm, welches einLoginfenster darstellen soll. Es verwendet dazu zwei verschiedene Layouttypen.Zum Einen das BorderLayout, um die Trennung zwischen dem Hauptbereich unddem Bereich mit den JButtons durchzufuhren und zum Anderen zwei BoxLayouts,welche den Hauptbereich und den Knopfebereich am unteren Rand organisieren.

import java . awt . BorderLayout ;import java . awt . Dimension ;import javax . swing . BorderFactory ;import javax . swing . Box ;import javax . swing . BoxLayout ;import javax . swing . JButton ;import javax . swing . JFrame ;import javax . swing . JLabel ;import javax . swing . JPanel ;import javax . swing . JPasswordField ;import javax . swing . JTextFie ld ;

/∗∗ Hinzuf ugen von JPanels und JLabe l s∗ und verwenden e ine s BoxLayouts∗/

class BoxLayoutFrame extends JFrame{public BoxLayoutFrame (){

super ( ”BoxLayout” ) ;s e tDe fau l tC loseOperat i on (

JFrame .EXIT ON CLOSE) ;

//BorderLayout f u r das Fenster e i n s t e l l e ngetContentPane ( ) . setLayout (

new BorderLayout ( ) ) ;

/∗∗ Hauptbere ich des JFrames e i n s t e l l e n∗/

//JPanel f u r den Hauptbere ich des JFramesJPanel mainPanel = new JPanel ( ) ;

2.1 Fenster und grafische Oberflachen 16

//BoxLayout f u r das mainPanel e i n s t e l l e nmainPanel . setLayout (

new BoxLayout (mainPanel , BoxLayout .PAGE AXIS ) ) ;

// JLabe l s und JTe x t f i e l d s zur EingabeJLabel l b lUs e r = new JLabel ( ”Login : ” ) ;JTextFie ld txtUser = new JTextFie ld ( ”User” ) ;JLabel l b lPa s s = new JLabel ( ”Password : ” ) ;JPasswordField txtPass = new JPasswordField ( ” pass ” ) ;

mainPanel . add ( l b lUs e r ) ;

mainPanel . add (Box . c reateRig idArea (new Dimension ( 0 , 5 ) ) ) ;

mainPanel . add ( txtUser ) ;

mainPanel . add (Box . c reateRig idArea (new Dimension ( 0 , 5 ) ) ) ;

mainPanel . add ( l b lPa s s ) ;

mainPanel . add (Box . c reateRig idArea (new Dimension ( 0 , 5 ) ) ) ;

mainPanel . add ( txtPass ) ;

/∗∗ But tonbere ich des JFrames e i n s t e l l e n∗/

//JPanel f u r d i e ButtonsJPanel buttonPanel = new JPanel ( ) ;

//BoxLayout f u r das but tonPane l e i n s t e l l e nbuttonPanel . setLayout (

new BoxLayout (buttonPanel , BoxLayout . LINE AXIS ) ) ;

//oben , l i n k s , unten , r e c h t sbuttonPanel . setBorder (

BorderFactory . createEmptyBorder ( 5 , 0 , 5 , 0 ) ) ;

// JButtons e r s t e l l e n

2.1 Fenster und grafische Oberflachen 17

JButton btnOk = new JButton ( ”Ok” ) ;JButton btnCancel = new JButton ( ”Cancel ” ) ;

buttonPanel . add (Box . c r ea teHor i zonta lG lue ( ) ) ;

buttonPanel . add (btnOk ) ;

buttonPanel . add (Box . c reateRig idArea (

new Dimension ( 5 , 0 ) ) ) ;

buttonPanel . add ( btnCancel ) ;

//Hinzuf ugen der JPanels zum BorderLayoutgetContentPane ( ) . add ( mainPanel ) ;getContentPane ( ) . add (

buttonPanel , BorderLayout .SOUTH) ;

s e t S i z e ( 220 , 160 ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}}public class FrameCreator07 {public stat ic void main ( St r ing [ ] a rgs ){

new BoxLayoutFrame ( ) ;}}

Nachdem in dem Quellcode ein JFrame erstellt wurde und fur das ContentPanedes JFrames das BorderLayout eingestellt wurde, erstellt das Programm das JPa-nel mainPanel. Dieses bekommt uber setLayout() ein BoxLayout zugewiesen. DerKonstruktor erhalt dazu das JPanel, welches vom Layout organisiert werden soll,sowie die grobe Orientierung als Konstante. Danach werden die JLabels und dasJTextField, sowie das JPasswordField erstellt, welche zur Texteingabe verwendetwerden konnen. Danach werden sie zum mainPanel hinzugefugt und mit RigidA-reas voneinander etwas abgetrennt. Die RigidAreas sorgen dafur, dass das Objekt,welches vorher eingefugt wurde 5 Pixel von dem Objekt entfernt liegt, welchesdanach eingefugt wird.

Die selbe Prozedur wird fur das buttonPanel durchgefuhrt, nur dass man hiereine horizontale Orientierung uber den BoxLayout-Konstruktor einstellt und vorden JButtons einen horizontalGlue einfugt. Dieser erstreckt sich uber den kom-pletten nicht genutzten Bereich der vor den JButtons liegt. Des weiteren wird fur

2.1 Fenster und grafische Oberflachen 18

das buttonPanel eine Border eingestellt. Dabei handelt es sich um einen Rand,welcher um alle Objekte des buttonPanels gezogen wird, so dass ein Abstand zwi-schen dem Rand des Fensters und dem mainPanel entsteht.

Zu guter letzt werden dann noch die beiden JPanels in das JFrame eingefugt.Dabei kommt das mainPanel in den zentralen Bereich des BorderLayouts unddas buttonPanel in den sudlichen Bereich. Das Ergebnis wird in Abbildung 2.7dargestellt.

Abbildung 2.7. Verwendung eines BoxLayouts zur Erstellung eines Login-Bildschirms.

GridBagLayout

Das GridBagLayout ist wohl das Layout, welches am schwierigsten zu verwen-den ist, jedoch bietet es dem Programmierer wohl die meisten Moglicheiten umgrafische Elemente auszurichten. Dabei verwendet das GridBagLayout ein Gitter,ahnlich wie das GridLayout. Der Unterschied besteht jedoch darin, dass sich ineinem GridBagLayout die Objekte uber mehrere Gitterzellen erstrecken konnen.Dabei berechnet Java automatisch anhand der minimalen und maximalen Großender Objekte wie hoch und breit die Zellen sein sollen. Zusatzlich kann man nochConstraints angeben, welche die Darstellung der einzelnen Objekte beeinflussenkonnen. Anders als bei den bisher verwendeten Layouts wird hier nicht einfachdas Layout zu einem Container hinzugefugt, sondern es wird noch weiterhin ver-wendet, um die Constraints zu jedem grafischen Objekt hinzufugen zu konnen.

Es gibt folgende Constraints:

• gridx & gridy - Geben an in welcher Zelle ein grafisches Objekt seinen Ursprungbesitzen soll.

• gridwidth & gridheight - Gibt an uber wieviele Zellen sich ein Objekt erstreckensoll.

• weightx & weighty - Gibt an wie breit und hoch die Zelle im Vergleich zu denanderen ist.

2.1 Fenster und grafische Oberflachen 19

• ipadx & ipady - Vergroßert ein Objekt in breite und/oder Hohe. Dabei ist dieVergroßerung ein Pixelwert und wird mit 2 multipliziert, da die Vergroßerungnach links und nach rechts bzw. nach oben und nach unten durchgefuhrt wird.

• fill - Falls ein Objekt kleiner ist als der Bereich den es ausfullen soll kannman angeben wie es sich vergroßern soll um den Bereich auszufullen. Es gibtdie konstanten Werte NONE, HORIZONTAL, VERTICAL und BOTH, welchealles statische Konstanten der Klasse GridBagLayout sind.

• insets - Mit Insets kann man einen Rahmen um ein Objekt legen. Diesen Ab-stand halt das Objekt dann als Rand zu den Zellen ein in denen sich das Objektbefindet.

• anchor - Dieser Wert gibt an, an welcher Stelle sich ein Objekt in seinen Zellenbefinden soll, wenn es kleiner als die Zellen ist. Es gibt neun Konstanten dieubergeben werden konnen und die jeweils eine Ecke der Zelle oder das Zen-trum darstellen. Die Konstanten sind FIRST LINE START, PAGE START,FIRST LINE END, LINE START, CENTER, LINE END, LAST LINE START,PAGE END und LAST LINE END, wobei beispielsweise FIRST LINE STARToben links ware und LAST LINE END unten rechts.

Im folgenden Beispiel sieht man dann wie man das Layout nutzen kann. Wegender Komplexitat des Layouts sollte man aber am besten einfach mit dem Layoutrumspielen um ein besseres Verstandnis fur dieses zu erhalten.

import java . awt . GridBagConstraints ;import java . awt . GridBagLayout ;import java . awt . I n s e t s ;import javax . swing . JButton ;import javax . swing . JComboBox ;import javax . swing . JFrame ;import javax . swing . JLabel ;import javax . swing . JScro l lPane ;import javax . swing . JTextArea ;import javax . swing . JTextFie ld ;

/∗∗ Hinzuf ugen von JPanels und JLabe l s und∗ verwenden e ine s GridBagLayouts∗/

class GridBagLayoutFrame extends JFrame{public GridBagLayoutFrame (){

super ( ”GridBagLayout” ) ;s e tDe fau l tC loseOperat i on (

JFrame .EXIT ON CLOSE) ;

// E i n s t e l l e n e ine s GridBagLayouts

2.1 Fenster und grafische Oberflachen 20

GridBagLayout gbl =new GridBagLayout ( ) ;

getContentPane ( ) . setLayout ( gbl ) ;

Hier sieht man einen gewissen Unterschied zu den vorherigen Layouts. Es wirdnicht nur eine Instanz von GridBagLayout erstellt, welche an die Methode set-Layout(Layout) ubergeben wird. Es wird stattdessen auch eine Variable gbl vomTyp GridBagLayout erstellt, an welche die Instanz vorher ubergeben wird. Diesist notig da man spater noch ofters auf das GridBagLayout zugreifen muss.

/∗∗ Hier wird e in GridBagConstraint−Objekt∗ e r s t e l l t∗/

GridBagConstraints gbc =new GridBagConstraints ( ) ;

/∗∗ E in s t e l l e n der Cons t ra in t s und∗ Hinzuf ugen der Objek te∗/

// Labe lgbc . gr idwidth =

GridBagConstraints .REMAINDER;gbc . g r i dhe i gh t = 1 ;gbc . weightx = 1 ;gbc . weighty = 1 ;gbc . anchor =

GridBagConstraints .LINE START;

//Konstruktor I n s e t s : oben , l i n k s , unten , r e c h t sgbc . i n s e t s = new I n s e t s ( 0 , 0 , 5 , 0 ) ;

JLabel label = new JLabel (”Autoborse : Kfz−Verkauf ” ) ;

gb l . s e tCon s t r a i n t s ( label , gbc ) ;getContentPane ( ) . add ( label ) ;

Hier sieht man nun wie ein Constraint erstellt wird. Dieser wird vor jedem Hin-zufugen eines Objekts, in die grafische Oberflache, eingestellt und fur das jeweiligeObjekt angepasst. Es wird beispielsweise die Anzahl der Zellen, welche das Objektbelegen soll uber das Constraint eingestellt und auch das Gewicht sowie der Ankerwerden festgelegt. Zusatzlich wird mit gbc.insets ein Rand um das Objekt erstellt.Das einzufugende Objekt soll ein JLabel sein, welches erstellt wird und dann zu-

2.1 Fenster und grafische Oberflachen 21

sammen mit dem Constraint an das Layout ubergeben wird. Zu guter letzt wirddas Objekt dann in den Container des JFrames eingefugt.

// Labe lgbc . gr idwidth = 1 ;gbc . i n s e t s = new I n s e t s ( 0 , 0 , 0 , 0 ) ;

label = new JLabel ( ”Fahrzeugtyp : ” ) ;gb l . s e tCon s t r a i n t s ( label , gbc ) ;getContentPane ( ) . add ( label ) ;

//ComboBox f u r Fahrzeugtypgbc . gr idwidth = GridBagConstraints .REMAINDER;gbc . f i l l = GridBagConstraints .HORIZONTAL;gbc . weightx = 3 ;gbc . i n s e t s = new I n s e t s ( 0 , 5 , 0 , 5 ) ;

S t r ing [ ] s t rArray = {”Sportwagen” ,”Limosine ” ,”Coupe” ,”Transporter ” ,”Bus” ,”Lkw” ,”Motorrad” } ;

JComboBox cmbType = new JComboBox( strArray ) ;gb l . s e tCon s t r a i n t s (cmbType , gbc ) ;getContentPane ( ) . add (cmbType ) ;

// Labe lgbc . gr idwidth = 1 ;gbc . i n s e t s = new I n s e t s ( 0 , 0 , 0 , 0 ) ;gbc . weightx = 1 ;

label = new JLabel ( ”Beschreibung : ” ) ;gb l . s e tCon s t r a i n t s ( label , gbc ) ;getContentPane ( ) . add ( label ) ;

//TextArea f u r d i e Beschreibunggbc . gr idwidth = GridBagConstraints .REMAINDER;gbc . g r i dhe i gh t = 3 ;gbc . f i l l = GridBagConstraints .BOTH;gbc . weightx = 10 ;

2.1 Fenster und grafische Oberflachen 22

gbc . weighty = 10 ;gbc . i n s e t s = new I n s e t s ( 0 , 5 , 0 , 5 ) ;

JTextArea txtExpl = new JTextArea ( ”Beschreibung ” ) ;JScro l lPane s c r o l lPane = new JScro l lPane ( txtExpl ) ;s c r o l lPane . s e tV e r t i c a l S c r o l lB a rPo l i c y (

JScro l lPane .VERTICAL SCROLLBAR ALWAYS) ;s c r o l lPane . s e tHo r i z on t a l S c r o l lBa rPo l i c y (

JScro l lPane .HORIZONTAL SCROLLBAR AS NEEDED) ;gbl . s e tCon s t r a i n t s ( s c ro l lPane , gbc ) ;getContentPane ( ) . add ( s c r o l lPane ) ;

// Labe lgbc . gr idwidth = 1 ;gbc . i n s e t s = new I n s e t s ( 0 , 0 , 0 , 0 ) ;gbc . weightx = 1 ;gbc . weighty = 1 ;gbc . f i l l = GridBagConstraints .NONE;

label = new JLabel ( ” Pre i s : ” ) ;gb l . s e tCon s t r a i n t s ( label , gbc ) ;getContentPane ( ) . add ( label ) ;

// Tex t f e l d f u r den Pre i sgbc . gr idwidth = GridBagConstraints .REMAINDER;gbc . f i l l = GridBagConstraints .HORIZONTAL;gbc . weightx = 3 ;gbc . i n s e t s = new I n s e t s ( 0 , 5 , 0 , 5 ) ;

JTextFie ld tx tPr i c e = new JTextFie ld ( ” 0 ,00 ” ) ;gb l . s e tCon s t r a i n t s ( txtPr i ce , gbc ) ;getContentPane ( ) . add ( tx tPr i c e ) ;

//Buttonsgbc . gr idwidth = 1 ;gbc . i n s e t s = new I n s e t s ( 0 , 0 , 0 , 0 ) ;gbc . weightx = 1 ;gbc . weighty = 1 ;

gbc . f i l l = GridBagConstraints .NONE;

JButton btnSend = new JButton ( ”Senden” ) ;gb l . s e tCon s t r a i n t s ( btnSend , gbc ) ;getContentPane ( ) . add ( btnSend ) ;

2.1 Fenster und grafische Oberflachen 23

JButton btnCancel = new JButton ( ”Abbrechen” ) ;gb l . s e tCon s t r a i n t s ( btnCancel , gbc ) ;getContentPane ( ) . add ( btnCancel ) ;

s e t S i z e ( 300 , 400 ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}}public class FrameCreator08 {public stat ic void main ( St r ing [ ] a rgs ){

new GridBagLayoutFrame ( ) ;}}

Die gesamte Prozedur wird fur jedes Objekt, der grafischen Oberflache durch-gefuhrt. Es ist also moglich mit einem GridBagLayout wesentlich komplexere Ober-flachen zu erstellen, als mit den vorher dargestellten Layouts. Aus diesem Grund istdas Layout jedoch etwas schwieriger zuganglich. Einen Screenshot von der Ober-flache, welche von dem obigen Quellcode erzeugt wird zeigt die Abbildung 2.8.

Abbildung 2.8. Mit einem GridBagLayout kann man komplexere grafische Ober-flachen erstellen.

2.1 Fenster und grafische Oberflachen 24

CardLayout

Das folgende CardLayout ist das einzige Layout in Java, welches mit dem Be-nutzer der grafischen Oberflache interagiert. Man kann sich das System dahinterso vorstellen, dass verschiedene Container, beispielsweise ein JPanel, Karten einesKartenspiels darstellen. Nimmt man nun die erste Karte vom Stapel kann mandie zweite sehen usw.. Dies gibt dem Benutzer die Moglichkeit die verschiedenenJPanels durchzublattern.

Das Layout an sich ist relativ einfach zu implementieren. Es wird ganz normal andie Methode setLayout(Layout) ubergeben, sollte jedoch vorher in einer Variablengespeichert werden, so dass man spater noch Zugriff darauf hat, um die Benut-zerinteraktionen durchzufuhren. Danach konnen dann uber add(Object,String) dieContainerklassen oder beliebige grafische Objekte zu dem Container, welcher dasCardLayout verwendet, ubergeben werden. Dabei kann uber den Stringparameterjedem Objekt ein Name zugewiesen werden, uber welchen man das Objekt spaterdirekt anzeigen lassen kann. Die Objekte werden dann hintereinander dargestelltund konnen nun uber die folgenden Funktionen durchgeblattert werden.

• next(Container) - Zeigt das nachste Element an• previous(Container) - Zeigt das vorherige Element an• first(Container) - Zeigt das erste Element an• last(Container) - Zeigt das letzte Element an• show(Container, String) - Zeigt ein bestimmtes Element direkt an

Der folgende Quellcode zeigt wie man relativ einfach, solch ein CardLayoutverwenden kann.

import java . awt . BorderLayout ;import java . awt . CardLayout ;import java . awt . FlowLayout ;import java . awt . event . ActionEvent ;import java . awt . event . Act i onL i s t ene r ;import javax . swing . JButton ;import javax . swing . JFrame ;import javax . swing . JPanel ;import javax . swing . JTextArea ;

/∗∗ Hinzuf ugen von JPanels und JLabe l s und verwenden∗ e ine s CardLayouts∗/

class CardFrame extends JFrame implements Act ionL i s t ene r {private JButton btnPrev ;private JButton btnNext ;private JButton btnF i r s t ;private JButton btnLast ;

2.1 Fenster und grafische Oberflachen 25

private JButton btn5 ;

private CardLayout cardLayout ;

Es ist wichtig, dass das CardLayout, wie hier gezeigt, fur die gesamte Klasseverfugbar ist, da in der Methode actionPerformed(ActionEvent) spater nochmaldarauf zugegriffen werden muss.

private JPanel pnlMain ;

public CardFrame (){s e tT i t l e ( ”CardLayout” ) ;s e tDe fau l tC loseOperat i on (

JFrame .EXIT ON CLOSE) ;

// E i n s t e l l e n e ine s BorderLayoutsgetContentPane ( ) . setLayout (

new BorderLayout ( ) ) ;

//Einf ugen von ButtonPanel und ShowPanelJPanel pnlButton = new JPanel ( ) ;pnlMain = new JPanel ( ) ;

getContentPane ( ) . add (pnlButton , BorderLayout .SOUTH) ;

getContentPane ( ) . add (pnlMain , BorderLayout .CENTER) ;

/∗∗ E in s t e l l e n der Layouts von∗ pnlButton und pnlMain∗/

pnlButton . setLayout (new FlowLayout ( ) ) ;

cardLayout = new CardLayout ( ) ;pnlMain . setLayout ( cardLayout ) ;

Hier wird fur das JPanel pnlMain das CardLayout eingerichtet. Es handelt sichdabei um das JPanel, welches die Textfelder anzeigt. Das CardLayout wird dabeiin die globale Variable cardLayout geschrieben, um spater noch darauf zugreifenzu konnen.

//Hinzuf ugen der Objek te zu den PanelsbtnPrev = new JButton ( ”<” ) ;

2.1 Fenster und grafische Oberflachen 26

btnNext = new JButton ( ”>” ) ;b tnF i r s t = new JButton ( ” Star t ” ) ;btnLast = new JButton ( ”Ende” ) ;btn5 = new JButton ( ”Mitte ” ) ;

pnlButton . add ( btnPrev ) ;pnlButton . add ( btnNext ) ;pnlButton . add ( btnF i r s t ) ;pnlButton . add ( btnLast ) ;pnlButton . add ( btn5 ) ;

btnPrev . addAct ionLis tener ( this ) ;btnNext . addAct ionLis tener ( this ) ;b tnF i r s t . addAct ionLis tener ( this ) ;btnLast . addAct ionLis tener ( this ) ;btn5 . addAct ionLis tener ( this ) ;

for ( int i =0; i <10; i++){pnlMain . add (new JTextArea (

”Karte ” + i ) , ”” + i ) ;}

Hier werden die einzelnen JButtons erzeugt, mit welchen man die JPanels durch-blattern kann. Ihnen werden ActionListener hinzugefugt. ActionListener werdenzur Interaktion mit grafischen Elementen benotigt und werden im Abschnitt 2.1.4naher erlautert. Des Weiteren werden JTextAreas erzeugt, welche in das pnlMainhinzugefugt werden. Diese Stellen spater die einzelnen Seiten unseres CardLayoutsdar.

s e t S i z e ( 400 , 500 ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}

public void act ionPerformed ( ActionEvent evt ){i f ( evt . getSource ( ) . equa l s ( btnNext ) ){

cardLayout . next ( pnlMain ) ;} else i f ( evt . getSource ( ) . equa l s ( btnPrev ) ){

cardLayout . p rev ious ( pnlMain ) ;} else i f ( evt . getSource ( ) . equa l s ( b tnF i r s t ) ){

cardLayout . f i r s t ( pnlMain ) ;} else i f ( evt . getSource ( ) . equa l s ( btnLast ) ){

cardLayout . l a s t ( pnlMain ) ;} else {

cardLayout . show ( pnlMain , ”5” ) ;}}}

2.1 Fenster und grafische Oberflachen 27

public class FrameCreator09 {public stat ic void main ( St r ing [ ] a rgs ){

new CardFrame ( ) ;}}

In der Methode actionPerformed(ActionEvent) werden Benutzereingaben abge-fangen und ausgewertet. Hier wird festgelegt, dass beispielsweise ein Klick auf denJButton btnNext die nachste Karte des CardLayouts anzeigt. Wie die Oberflacheaussieht, wenn man den Quellcode ausfuhrt zeigt die Abbildung 2.9.

Abbildung 2.9. Das CardLayout ist das einzige Layout in Java, welches Interak-tionen des Benutzers zulasst.

Weitere Informationen zu den hier erlauterten Layouts konnen in”A Visual

Guide to Layout Managers“ ([Javb]) von Sun nachgeschlagen werden.

2.1 Fenster und grafische Oberflachen 28

2.1.4 Verwenden von Listenern

Listener dienen zur Kommunikation zwischen dem Benutzer eines Programms unddem Programm selbst. Wenn ein Benutzer mit einem grafischen Element, beispiels-weise einem JButton oder einer JList, interagiert, werden bestimmte Methodenausgefuhrt. Diese Methoden kann der Programmierer uberschreiben und somitseine eigenen Aktionen programmieren, welche bei einem Klick auf einen JButtono.a. ausgefuhrt werden.

Zu diesem Zweck existieren mehrere Listener. Die folgende Liste enthalt wohldie wichtigsten Listener die man benotigt um eine grafische Oberflache zu erstellen.Weitere findet man in der Java API [Javc]:

• ActionListener• ChangeListener• ItemListener• KeyListener• ListDataListener• ListSelectionListener• MouseListener• MouseMotionListener• ItemListener• etc.

Alle diese Listener besitzen verschiedene Funktionen, welche aufgerufen wer-den, wenn eine bestimmte Benutzereingabe erfolgt. Diese hangen oftmals auchvon bestimmten grafischen Elementen ab. So benutzt man beispielsweise den Ac-tionListener um bei JButtons auf Eingaben zu reagieren, ein MouseListener wirdhingegen fur Mauseingaben verwendet.

In dem folgenden Beispielquellcode wird gezeigt auf welche verschiedenen Artenman mit einem einfachen ActionListener arbeiten kann.

import java . awt . GridLayout ;import java . awt . event . ActionEvent ;import java . awt . event . Act i onL i s t ene r ;import javax . swing . JButton ;import javax . swing . JFrame ;import javax . swing . JOptionPane ;

/∗∗ Er s t e l l e n e ine s Fens ters mit e iner automatischen Groesse∗/

class ListenerFrame extends JFrame implements Act ionL i s t ene r {private JButton btn3a ;private JButton btn3b ;

2.1 Fenster und grafische Oberflachen 29

public ListenerFrame (){super ( ”ListenerFrame” ) ;s e tDe fau l tC loseOperat i on (JFrame .EXIT ON CLOSE) ;

//Layout e i n s t e l l e nsetLayout (new GridLayout ( 0 , 2 ) ) ;

//Buttons h inzu f u genJButton btn1a = new JButton ( ”1a” ) ;JButton btn1b = new JButton ( ”1b” ) ;

JButton btn2a = new JButton ( ”2a” ) ;btn2a . setActionCommand ( ”a” ) ;JButton btn2b = new JButton ( ”2b” ) ;btn2b . setActionCommand ( ”b” ) ;

btn3a = new JButton ( ”3a” ) ;btn3b = new JButton ( ”3b” ) ;

// Act ionLi s t ener h inzu f u genbtn1a . addAct ionLis tener ( this ) ;btn1b . addAct ionLis tener ( this ) ;btn2a . addAct ionLis tener ( this ) ;btn2b . addAct ionLis tener ( this ) ;btn3a . addAct ionLis tener ( this ) ;btn3b . addAct ionLis tener ( this ) ;

//Einf ugen der Buttons in das FramegetContentPane ( ) . add ( btn1a ) ;getContentPane ( ) . add ( btn1b ) ;getContentPane ( ) . add ( btn2a ) ;getContentPane ( ) . add ( btn2b ) ;getContentPane ( ) . add ( btn3a ) ;getContentPane ( ) . add ( btn3b ) ;

pack ( ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}

public void act ionPerformed ( ActionEvent evt ){/∗∗ Es g i b t ve r sch i edene Mog l i chke i t en herauszuf inden ,∗ welcher Button gedr u ck t wurde .

2.1 Fenster und grafische Oberflachen 30

∗/

// Verg l e i ch mit der Beschr i f t ung des Buttonsi f ( ( ( JButton ) evt . getSource ( ) ) . getText ( ) . equa l s ( ”1a” ) ){

JOptionPane . showMessageDialog (null , ” S i e haben den Button 1a gedr uckt ! ” ,”Gedruckt” , JOptionPane .INFORMATION MESSAGE) ;

} else i f ( ( ( JButton ) evt . getSource ( ) ) . getText ( ) . equa l s ( ”1b” ) ){JOptionPane . showMessageDialog (

null , ” S i e haben den Button 1b gedr uckt ! ” ,”Gedruckt” , JOptionPane .INFORMATION MESSAGE) ;

}

// Verg l e i ch mit dem ActionCommandi f ( ( ( JButton ) evt . getSource ( ) ) . getActionCommand ( ) . equa l s ( ”a” ) ){

JOptionPane . showMessageDialog (null , ” S i e haben den Button 2a gedr uckt ! ” ,”Gedruckt” , JOptionPane .INFORMATION MESSAGE) ;

} else i f ( ( ( JButton ) evt . getSource ( ) ) . getActionCommand ( ) . equa l s ( ”b” ) ){JOptionPane . showMessageDialog (

null , ” S i e haben den Button 2b gedr uckt ! ” ,”Gedruckt” , JOptionPane .INFORMATION MESSAGE) ;

}

// Verg l e i ch mit den e i g e n t l i c h e n Objekteni f ( evt . getSource ( ) == btn3a ){

JOptionPane . showMessageDialog (null , ” S i e haben den Button 3a gedr uckt ! ” ,”Gedruckt” , JOptionPane .INFORMATION MESSAGE) ;

} else i f ( evt . getSource ( ) == btn3b ){JOptionPane . showMessageDialog (

null , ” S i e haben den Button 3b gedr uckt ! ” ,”Gedruckt” , JOptionPane .INFORMATION MESSAGE) ;

}}}

public class FrameCreator10{public stat ic void main ( St r ing [ ] a rgs ){

new ListenerFrame ( ) ;}}

Wenn man diesen Quellcode ausfuhrt erhalt man ein neues JFrame mit sechsverschiedenen JButtons. Diese werden, wie bereits in vorherigen Abschnittenerlautert, per GridLayout in das JFrame eingefugt. Wichtig ist dabei, dass dieVariable mit den JButtons 3a und 3b in der Klasse verfugbar sind, da sie inder Methode actionPerformed(ActionEvent) verwendet werden. Die actionPerfor-

2.1 Fenster und grafische Oberflachen 31

med(ActionEvent) Methode ist eine Methode der implementierten SchnittstelleActionListener. Sie wird ausgefuhrt, wenn ein Benutzer eine Aktion ausfuhrt. Umfestzulegen, welcher ActionListener zu welchem Button gehort wird dem jeweiligenButton mit der Methode addActionListener(this) der ActionListener ubergeben.Das this kann verwendet werden, da man sich zur Zeit in der Klasse befindet,welche den ActionListener implementiert.

Die Methode actionPerformed(ActionEvent) bekommt per Argument ein so ge-nanntes ActionEvent ubergeben. Dieses ist ein Objekt, welches verschiedene In-formationen uber die Aktion, welche ausgefuhrt wurde, mit sich bringt. Die inter-essantesten Daten des ActionEvents werden in der actionPerformed(ActionEvent)Methode des Beispielquellcodes benutzt um herauszufinden, welcher Button ge-druckt wurde. Die Buttons mit der Beschriftung 1a und 1b werden anhand ihrerBeschriftung identifiziert. Dazu werden die Methoden getSource() und getText()verwendet, welche zu dem ActionEvent-Objekt, sowie dem JButton gehoren. Mitder Methode getSource() erhalt man das grafische Objekt, welches das ActionEventausgelost hat und mit getText() erhalt man dann die Beschriftung des jeweiligenButtons. Diese werden dann mit den Textstrings ’1a’ und ’1b’ verglichen und dasProgramm weiß, welcher Button gedruckt wurde.

Man kann sich nun vorstellen, dass sich solch eine Beschriftung, wahrend derProgrammierung, einmal andern kann oder dass man vielleicht mit Sprachdateienoder etwas ahnlichem die Beschriftung wahrend der Laufzeit andert. Dies wurdedazu fuhren, dass man den Button nicht mehr oder nur sehr schwierig, uber sei-ne Beschriftung identifizieren kann. Stattdessen kann man jedoch jedem Buttoneinen ActionCommand zuweisen. Dies geschieht mit der Methode setActionCom-mand(String). Nun besteht die Moglichkeit, dass man mit diesem Wert vergleichtum die Identifizierung des grafischen Elements durchzufuhren. Dies geschieht mitden JButtons 2a und 2b.

Außer diesen Methoden gibt es noch eine weitere, welche fur die JButtons 3aund 3b verwendet wird. Hier wird direkt mit dem grafischen Element selbst ver-glichen. Dazu werden die grafischen Elemente, welche einen ActionListener verwen-den sollen, global bekannt gemacht. Dann wird in der actionPerformed(ActionEvent)-Methode der Ruckgabewert der getSource()-Methode des ActionEvents mit demglobalen Objekt verglichen. Sind diese identisch hat man das jeweilige Objekt her-ausgefunden und kann darauf reagieren.

Eine vierte Methode, welche hier nicht vorgestellt wird, jedoch einfach durch-zufuhren ist, ware fur jedes Objekt eine eigene Klasse zu schreiben, welche Action-Listener implementiert und diese dem jeweiligen JButton zu ubergeben. Somithatte jeder JButton einen eigenen ActionListener und man musste gar nicht ver-gleichen.

2.1 Fenster und grafische Oberflachen 32

Der vorgestellte Quellcode befindet sich auch noch einmal in der Datei Frame-Creator10.java.

Der nachste Quellcode ist dazu gedacht, das identische Prinzip nochmals an ei-nem anderen Listener deutlich zu machen. Es handelt sich um ein Beispiel fur einenMouseListener und einen MouseMotionListener, mit welchen man Mausklicks undMausbewegungen abfangen kann.

import java . awt . BorderLayout ;import java . awt . event . MouseEvent ;import java . awt . event . MouseListener ;import java . awt . event . MouseMotionListener ;import javax . swing . JFrame ;import javax . swing . JLabel ;import javax . swing . JPanel ;

/∗∗ Er s t e l l e n e ine s Fens ters mit einem Panel , we lches∗ einen MouseListener und einen MouseMotionListener∗ verwendet um Mousebewegungen und −k l i c k s abzufangen .∗/

class MouseListenerFrame extends JFrameimplements MouseListener , MouseMotionListener{private int cl ickedXCoord=0;private int cl ickedYCoord=0;private int movedXCoord=0;private int movedYCoord=0;

private JLabel output ;

public MouseListenerFrame (){super ( ”MouseListener und ” +

”MouseMotionListener ” ) ;s e tDe fau l tC loseOperat i on (

JFrame .EXIT ON CLOSE) ;

// S t e l l t das BorderLayout f u r das Frame eingetContentPane ( ) . setLayout (

new BorderLayout ( ) ) ;

//Das Panel , we lches einen MouseListener e r ha l t en s o l lJPanel panel = new JPanel ( ) ;

2.1 Fenster und grafische Oberflachen 33

//Hinzuf ugen e ine s MouseListenerspanel . addMouseListener ( this ) ;

//Hinzuf ugen e ine s MouseMotionListenerspanel . addMouseMotionListener ( this ) ;

//Hinzuf ugen des PanelsgetContentPane ( ) . add (

panel ,BorderLayout .CENTER) ;

// Labe l zum Anzeigen von Ausgabenoutput = new JLabel ( ”Ausgabe : ” ) ;

//Hinzuf ugen des Te x t f e l d sgetContentPane ( ) . add (

output ,BorderLayout .SOUTH) ;

s e t S i z e ( 400 , 300 ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}

//Methoden des MouseListenerspublic void mouseClicked (MouseEvent evt ){

cl ickedXCoord = evt . getX ( ) ;cl ickedYCoord = evt . getY ( ) ;

output . setText (”Move : ” + movedXCoord +”/” + movedYCoord +” − Cl ick : ” + clickedXCoord +”/” + clickedYCoord ) ;

}public void mouseEntered (MouseEvent evt ){}public void mouseExited (MouseEvent evt ){}public void mousePressed (MouseEvent evt ){}public void mouseReleased (MouseEvent evt ){}

2.1 Fenster und grafische Oberflachen 34

//Methoden des MouseMotionListenerspublic void mouseDragged (MouseEvent evt ){}public void mouseMoved(MouseEvent evt ){

movedXCoord = evt . getX ( ) ;movedYCoord = evt . getY ( ) ;

output . setText (”Move : ” + movedXCoord +”/” + movedYCoord +” − Cl ick : ” + clickedXCoord +”/” + clickedYCoord ) ;

}}

public class FrameCreator11{public stat ic void main ( St r ing [ ] a rgs ){

new MouseListenerFrame ( ) ;}}

Wenn man diesen Quellcode ausfuhrt (der Quellcode ist in der Datei Frame-Creator11.java enthalten), erhalt man ein Fenster mit einer großen leeren Flache,welche von einem JPanel realisiert wird und einem JLabel, am unteren Bildschirm-rand, uber welches verschiedene Ausgaben gemacht werden konnen.

Der MouseListener und der MouseMotionListener in diesem Beispiel werdendafur verwendet um Koordinaten auszugeben, an welchen sich gerade die Mausbefindet und an welchen zuletzt geklickt wurde. Diese werden, wie im vorheri-gen Beispiel mit dem ActionListener, einfach an das passende Objekt ubergeben,auf welchem der Listener verwendet werden soll. In diesem Fall handelt es sichdabei um das JPanel, auf welchem die Maus bewegt werden soll. Dies geschiehtuber die Methoden addMouseListener(MouseListener) und addMouseMotionLis-tener(MouseMotionListener) des JPanels.

Der Rest der Interaktion wird uber die einzelnen implementierten Methoden,welche von den beiden Schnittstellen angeboten werden, durchgefuhrt. Dies sinddie Methoden mouseClicked(MouseEvent), mouseEntered(MouseEvent), mouseE-xited(MouseEvent), mousePressed(MouseEvent) und mouseReleased(MouseEvent)des MouseListeners und die Methoden mouseDragged(MouseEvent) und mou-seMoved(MouseEvent) des MouseMotionListeners. Die Methode mouseClicked(MouseEvent) wird beispielsweise aktiviert, wenn man einen Mausklick durchfuhrtund gibt, in diesem Beispiel, die zuletzt angeklickte Koordinate aus. Da es sich beiMouseListener und MouseMotionListener um Schnittstellen handelt, mussen alleMethoden komplett implementiert werden. Dies ist oftmals etwas umstandlich undunnotig, da man meistens nicht alle Methoden benotigt.

2.1 Fenster und grafische Oberflachen 35

Um die unnotige Impelementierung aller Schnittstellenmethoden zu verhindernkann man auch statt der Schnittstellen so genannte Adapterklassen verwenden.Diese werden in dem folgenden Quellcode vorgestellt, welcher auch nochmal inder Datei FrameCreator12.java eingesehen werden kann. Sie erfullen im eigentli-chen Sinn den gleichen Zweck wie die Schnittstellen, jedoch handelt es sich dabeium Klassen, welche von einer der obersten Adapterklassen erben und somit dieMethoden nicht zwingend implementieren mussen. Stattdessen uberschreibt mandie Methoden der Oberklasse, welche man benotigt. Danach wird dann eine In-stanz dieser Adapterklasse an ein Objekt ubergeben. Dies geschieht, genau wiebei den Schnittstellen uber die Methoden addMouseListener(MouseAdapter) undaddMouseMotionListener(MouseMotionAdapter).

Die Klassen MouseAdapter und MouseMotionAdapter enthalten die gleichenMethoden wie die passenden Schnittstellen. Im Beispielcode werden die MethodenmouseClicked(MouseEvent) und mouseMoved(MouseEvent) uberschrieben um diezuletzt angeklickten und zuletzt anvisierten Mauskoordinaten auszugeben.

import java . awt . BorderLayout ;import java . awt . event . MouseAdapter ;import java . awt . event . MouseEvent ;import java . awt . event . MouseMotionAdapter ;import javax . swing . JFrame ;import javax . swing . JLabel ;import javax . swing . JPanel ;

/∗∗ Er s t e l l e n e ine s Fensters , we lches s t a t t einem Li s t ene r∗ e ine Klasse verwendet , we lche von der Klasse MouseAdapter∗ e r b t .∗/

class MouseAdapterFrame extends JFrame{private int cl ickedXCoord=0;private int cl ickedYCoord=0;private int movedXCoord=0;private int movedYCoord=0;

private JLabel output ;

2.1 Fenster und grafische Oberflachen 36

public MouseAdapterFrame (){super (

”Verwendung e i n e s MouseAdapters” ) ;s e tDe fau l tC loseOperat i on (

JFrame .EXIT ON CLOSE) ;

// S t e l l t das BorderLayout f u r das Frame eingetContentPane ( ) . setLayout (

new BorderLayout ( ) ) ;

//Das Panel , we lches einen MouseListener e r ha l t en s o l lJPanel panel = new JPanel ( ) ;

//Hinzuf ugen e ine s MouseListenerspanel . addMouseListener (

new MouseAdapterClass ( this ) ) ;

//Hinzuf ugen e ine s MouseMotionListenerspanel . addMouseMotionListener (

new MouseMotionAdapterClass ( this ) ) ;

//Hinzuf ugen des PanelsgetContentPane ( ) . add ( panel , BorderLayout .CENTER) ;

// Labe l zum Anzeigen von Ausgabenoutput = new JLabel ( ”Ausgabe : ” ) ;

//Hinzuf ugen des Te x t f e l d sgetContentPane ( ) . add ( output , BorderLayout .SOUTH) ;

s e t S i z e ( 400 , 300 ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}

//Gibt das AusgabePanel zur uckpublic JLabel getOutput ( ){

return output ;}

//Gibt d i e l e t z t e g e k l i c k t e x Koordinate zur uckpublic int getClickedXCoord ( ) {

return cl ickedXCoord ;}

2.1 Fenster und grafische Oberflachen 37

// S t e l l t d i e l e t z t e g e k l i c k t e x Koordinate e inpublic void setClickedXCoord ( int cl ickedXCoord ) {

this . c l ickedXCoord = clickedXCoord ;}

//Gibt d i e l e t z t e g e k l i c k t e y Koordinate zur uckpublic int getClickedYCoord ( ) {

return cl ickedYCoord ;}

// S t e l l t d i e l e t z t e g e k l i c k t e x Koordinate e inpublic void setClickedYCoord ( int cl ickedYCoord ) {

this . c l ickedYCoord = clickedYCoord ;}

//Gibt d i e l e t z t e x Koordinate zur uckpublic int getMovedXCoord ( ) {

return movedXCoord ;}

// S t e l l t d i e l e t z t e x Koordinate e inpublic void setMovedXCoord ( int movedXCoord) {

this . movedXCoord = movedXCoord ;}

//Gibt d i e l e t z t e y Koordinate zur uckpublic int getMovedYCoord ( ) {

return movedYCoord ;}

// S t e l l t d i e l e t z t e y Koordinate e inpublic void setMovedYCoord ( int movedYCoord) {

this . movedYCoord = movedYCoord ;}}

//Klasse welche den MouseListener e r s e t z tclass MouseAdapterClass extends MouseAdapter{private MouseAdapterFrame maf ;public MouseAdapterClass (MouseAdapterFrame maf ){

this . maf = maf ;}

2.1 Fenster und grafische Oberflachen 38

public void mousePressed (MouseEvent evt ){maf . setClickedXCoord ( evt . getX ( ) ) ;maf . setClickedYCoord ( evt . getY ( ) ) ;

maf . getOutput ( ) . setText (”Move : ” + maf . getMovedXCoord ( ) +”/” + maf . getMovedYCoord ( ) +” − Cl ick : ” + maf . getClickedXCoord ( ) +”/” + maf . getClickedYCoord ( ) ) ;

}}

//Klasse welche den MouseMotionListener e r s e t z tclass MouseMotionAdapterClass extends MouseMotionAdapter{private MouseAdapterFrame maf ;public MouseMotionAdapterClass (MouseAdapterFrame maf ){

this . maf = maf ;}public void mouseMoved(MouseEvent evt ){

maf . setMovedXCoord ( evt . getX ( ) ) ;maf . setMovedYCoord ( evt . getY ( ) ) ;

maf . getOutput ( ) . setText (”Move : ” + maf . getMovedXCoord ( ) +”/” + maf . getMovedYCoord ( ) +” − Cl ick : ” + maf . getClickedXCoord ( ) +”/” + maf . getClickedYCoord ( ) ) ;

}}

public class FrameCreator12{public stat ic void main ( St r ing [ ] a rgs ){

new MouseAdapterFrame ( ) ;}}

Man sieht also, dass es eigentlich relativ einfach ist solche Interaktionen zwi-schen dem Benutzer und dem Programm zu implementieren. Dabei sollte daraufgeachtet werden, dass man immer die korrekten Listener verwendet, welche fur einbestimmtes grafisches Objekt zur Verfugung stehen und das man sich uberlegt, wel-ches Prinzip man verwendet um herauszufinden, welche Buttons o.a. uberhauptangeklickt wurden bzw. welches grafische Objekt eine Aktion ausfuhrt.

2.1 Fenster und grafische Oberflachen 39

2.1.5 Erstellen von Menuleisten

Abbildung 2.10. Eine Menuleiste, wie sie in fast jedem Programm vorkommt.

In dem folgenden Abschnitt wird erlautert, wie man auf einfache Art und Weiseeine Menuleiste erstellen kann, wie sie in der Abbildung 2.10 gezeigt wird.

Dazu gibt es verschiedene Klassen, welche benotigt werden um eine Menuleisteins Leben zu rufen. Als erste Struktur ware die JMenuBar zu nennen. Bei der JMe-nuBar handelt es sich um die eigentliche Leiste, welche weitere Untermenus oderKnopfe beinhalten kann. Dann gibt es die so genannten JMenus. Dabei handelt essich um Menus bzw. Untermenus. Diese konnen per add-Methode des JMenuBarzu der Leiste hinzugefugt werden. Ein JMenu hat des Weiteren ebenfalls eine eigeneadd-Methode, mit welcher weitere JMenus und JMenuItems hinzugefugt werdenkonnen. Ein JMenuItem ahnelt einem Button, auf welchen man klicken kann. ImNormalfall werden mogliche Listener an ein JMenuItem ubergeben um von dortaus bestimmte Funktionen auszufuhren, wenn auf ein JMenuItem geklickt wird.Hat man eine JMenuBar mit passenden JMenus und JMenuItems erstellt und mitden add-Methoden zusammengefugt kann man die JMenuBar mit der MethodesetJMenuBar eines JFrames, zu diesem hinzufugen. Der folgende Quellcode, wel-cher nochmals in der Datei FrameCreator13.java enthalten ist, zeigt die kompletteProzedur etwas ausfuhrlicher.

2.1 Fenster und grafische Oberflachen 40

import javax . swing . JFrame ;import javax . swing . JMenu ;import javax . swing . JMenuBar ;import javax . swing . JMenuItem ;

/∗∗ Er s t e l l e n e ine s Fens ters mit e iner automatischen Groesse∗/

class MenuFrame extends JFrame{public MenuFrame(){

super ( ”JFrame mit Menu” ) ;s e tDe fau l tC loseOperat i on (

JFrame .EXIT ON CLOSE) ;

// E r s t e l l e n e iner Menu l e i s teJMenuBar menuBar = new JMenuBar ( ) ;

//Hinzuf ugen von MenusJMenu menuFile =

new JMenu( ”Datei ” ) ;JMenu menuEdit =

new JMenu( ”Bearbe i ten ” ) ;JMenu menuHelp =

new JMenu( ” H i l f e ” ) ;

menuBar . add ( menuFile ) ;menuBar . add (menuEdit ) ;menuBar . add (menuHelp ) ;

Zuerst wird eine JMenuBar erstellt, in welche man spater JMenus und JMe-nuItems hinzufugen kann. Danach werden die JMenus erstellt. Sie erhalten perKonstruktor die Namen ’Datei’, ’Bearbeiten’ und ’Hilfe’ und werden danach peradd-Methode zu der Menuleiste hinzugefugt.

//Hinzuf ugen von UntermenusJMenu menuFileNew =

new JMenu( ”Neu” ) ;

menuFile . add (menuFileNew ) ;

//Hinzuf ugen von Menueintragen in das DateimenuJMenuItem menuItemFileNewText =

new JMenuItem( ”Text” ) ;JMenuItem menuItemFileNewImage =

new JMenuItem( ”Bi ld ” ) ;JMenuItem menuItemFileOpen =

2.1 Fenster und grafische Oberflachen 41

new JMenuItem( ” Offnen” ) ;JMenuItem menuItemFileSave =

new JMenuItem( ” Spe ichern ” ) ;JMenuItem menuItemFileSaveAs =

new JMenuItem( ” Spe ichern a l s ” ) ;JMenuItem menuItemFileExit =

new JMenuItem( ”Beenden” ) ;

menuFileNew . add ( menuItemFileNewText ) ;menuFileNew . add ( menuItemFileNewImage ) ;menuFile . add ( menuItemFileOpen ) ;menuFile . add ( menuItemFileSave ) ;menuFile . add ( menuItemFileSaveAs ) ;menuFile . addSeparator ( ) ;menuFile . add ( menuItemFileExit ) ;

Hier werden nun weitere Untermenus und JMenuItems erstellt, welche in dasJMenu ’Datei’ eingefugt werden. Des Weiteren wird ein Untermenu ’Neu’ erstellt,in welches wiederum JMenuItems eingefugt werden.

/∗∗ Hinzuf ugen von Menueintragen∗ in das Bearbeitenmenu∗/

JMenuItem menuItemEditCut =new JMenuItem( ”Ausschneiden” ) ;

JMenuItem menuItemEditCopy =new JMenuItem( ”Kopieren” ) ;

JMenuItem menuItemEditPaste =new JMenuItem( ”Einf ugen ” ) ;

menuEdit . add ( menuItemEditCut ) ;menuEdit . add (menuItemEditCopy ) ;menuEdit . add ( menuItemEditPaste ) ;

//Hinzuf ugen von Menueintragen in das HilfemenuJMenuItem menuItemHelpHelp =

new JMenuItem( ” H i l f e ” ) ;

menuHelp . add ( menuItemHelpHelp ) ;

//Hinzuf ugen der Menu l e i s te zum FramesetJMenuBar (menuBar ) ;

Des Weiteren werden noch Eintrage in die JMenus ’Bearbeiten’ und ’Hilfe’eingefugt und zu guter letzt wird mit dem Befehl setJMenuBar die oben erstellteMenuleiste als Menuleiste fur dieses Fenster festgelegt.

2.2 Applets 42

s e t S i z e ( 400 , 300 ) ;s e tLocat i on ( 5 0 , 5 0 ) ;s e tV i s i b l e ( true ) ;

}}

public class FrameCreator13{public stat ic void main ( St r ing [ ] a rgs ){

new MenuFrame ( ) ;}}

Man sieht also, wie einfach es ist eine eigene Menuleiste zu erstellen. Fur weitereInformationen sollte die Java API ([Javc]) zu rate gezogen werden.

2.2 Applets

Applets sind kleine Javaprogramme, die direkt in Webbrowsern ausfuhrbar sind.Das bedeutet, dass diese Programme in beliebige HTML-Dokumente eingebun-den werden konnen. Hierbei gibt es jedoch einige Unterschiede im Vergleichzu normalen Anwendungen. Um Applets zu programmieren muss das Packa-ge

”java.applet.*“ importiert werden. Wenn Swing-Elemente benotigt werden

muss ein JApplet implementiert werden. Dazu wird jedoch das Package”ja-

vax.swing.JApplet“ verwendet. Die Klassen mit dem eigentlichen Programmcodeerweitern dann Applet bzw. JApplet.Ein Unterschied zu normalen Anwendungen findet sich bei der Verwendung derBenutzeroberflache. In Anwendungen muss erst einmal ein Frame fur die Aufnah-me der Komponenten erstellt werden. In diesen werden dann meist noch einigePanels gelegt. Im Unterschied dazu konnen die Elemente in Applets direkt ein-gefugt werden.

import java . app le t . ∗ ;import java . awt . Graphics ;

public class Hel loApplet extends Applet{

public void paint ( Graphics g ) {g . drawString ( ” He l lo World” , 0 , 1 0 ) ;

}}

2.2 Applets 43

Abbildung 2.11. Hello World Applet in einem Webbrowser

Der wichtigste Unterschied zu”normalen“ Java Anwendungen ist, dass Applets

keine Main-Methode besitzen. Stattdessen gibt es hier vier Methoden die Mei-lensteine bei der Programmausfuhrung betreffen. Hierbei handelt es sich um dieMethoden:

1.”Init“�Diese Methode wird aufgerufen sobald das Applet zum ersten mal geladen wird.Hier sollen alle zur Ausfuhrung des Programms benotigten Initialisierungengeschehen.

2.”Start“�Die Start Methode wird ausgefuhrt, sobald die

”Init“ Methode abgeschlossen

ist, oder wenn das Applet wieder in das Blickfeld des Betrachters gerat. Dieseserneute Starten ist notig, wenn das Applet gestoppt wurde.

3.”Stop“�Die Methode

”Stop“ wird aufgerufen, wenn dass Applet aus dem Sichtbereich

des Anwenders verschwindet. Dies kann benotigt werden, wenn z.B. eine Ani-mation ausgefuhrt wird. Sobald der Anwender nicht mehr auf die Animationblicken kann, kann die dafur benotigte Rechenleistung freigegeben werden. Dasgeschieht, indem die Animation angehalten wird. Sobald das Applet wieder indas Blickfeld des Anwenders kommt wird wieder die

”Start“ Methode aufgeru-

fen.4.

”Destroy“�Wenn der Anwender die Seite mit dem Applet komplett verlasst wird die Me-thode

”Destroy“ aufgerufen. Hierin konnen alle vom Programm verwendeten

2.2 Applets 44

Ressourcen wieder freigegeben werden. Sollte das Applet spater noch einmalgestartet werden, so wird es auch neu initialisiert.

Eine Beispielanwendung zu diesen vier Methoden gibt bei jedem Methoden-aufruf den Namen der Funktion aus. Um jedoch wirklich alle Ausgaben sehen zukonnen muss eine Konsole geoffnet werden, da dass Applet geschlossen ist bevordie Destroy Anweisung ausgegeben werden kann.

import javax . swing . JApplet ;import java . awt . Graphics ;

public class Mile s tones extends JApplet{St r i ngBu f f e r s ;

// Methodenaufruf b e i der I n i t i a l i s i e r u n gpublic void i n i t ( ) {

s = new St r i ngBu f f e r ( ) ;s . append ( ” I n i t ! ” ) ;System . out . p r i n t l n ( s+” ” ) ;

}//Methodenaufruf wenn das Applet in den S i c h t b e r e i c h ge r a t

public void s t a r t ( ) {s . append ( ” Star t ! ” ) ;System . out . p r i n t l n ( s+” ” ) ;

}//Methodenaufruf wenn das Applet den S i c h t b e r e i c h v e r l a s s t

public void stop ( ) {s . append ( ”Stop ! ” ) ;System . out . p r i n t l n ( s+” ” ) ;

}//Methodenaufruf wenn das Applet g e s ch l o s s en wird

public void des t roy ( ) {s . append ( ”Destroy ! ” ) ;System . out . p r i n t l n ( s+” ” ) ;s=null ;

}//Methode zum Zeichnen des I n h a l t e s

public void paint ( Graphics g ) {g . drawString ( s . t oS t r i ng ( ) , 0 , 1 0 ) ;

}}

Allerdings ist es auch moglich ein Programm in Java so zu schreiben, dass essowohl als Applet als auch als Anwendung gestartet werden kann. Dazu wird dereigentliche Programmteil in eine Klasse geschrieben die von JPanel abgeleitet ist.

2.2 Applets 45

Abbildung 2.12. Anzeige der verschiedenen Meilensteine

Das hieraus entstehende Panel muss dann in der init Methode in das Applet, undin einer main Methode in ein Frame der Anwendung eingefugt werden.

public class AppletApp extends JApplet{

public void i n i t ( ){this . add (new MeinPanel ( ) ) ;

}

public stat ic void main ( St r ing [ ] a rgs ) {JFrame f e n s t e r = new JFrame

( ”Anwendungsfenster ” ) ;f e n s t e r . s e tDe fau l tC lo seOperat i on

( JFrame .EXIT ON CLOSE) ;f e n s t e r . s e t S i z e ( 200 , 200 ) ;f e n s t e r . add (new MeinPanel ( ) ) ;f e n s t e r . s e tV i s i b l e ( true ) ;

}}

2.2 Applets 46

class MeinPanel extends JPanel implements Act ionL i s t ene r {private stat ic int count=0;private JLabel anze ige ;

public MeinPanel ( ){this . setLayout (new BorderLayout ( ) ) ;

JButton knopf = new JButton ( ”Druck mich ! ” ) ;knopf . setActionCommand ( ”knopf ” ) ;knopf . addAct ionLis tener ( this ) ;this . add ( knopf , BorderLayout .SOUTH) ;

anze ige = new JLabel ( ””+count ) ;this . add ( anze ige , BorderLayout .CENTER) ;

}

public void act ionPerformed ( ActionEvent evt ){count++;anze ige . setText ( ””+count ) ;

}}

Der wichtigste Nutzen von Applets ist, dass sie einfach in Webseiten integriertwerden konnen. Dazu dient das ¡applet¿ Tag in HTML Seiten. Hier muss der Nameder ausfuhrbaren Datei, in der Regel mit der Endung

”.class“, angegeben werden.

Außerdem muss die Große des Applets angegeben sein. Zusatzlich konnen nochein horizontaler bzw. vertikaler Abstand zum Seitenrand angegeben werden, mithgap bzw. vgap. Falls das Applet auf einem Rechner nicht angezeigt werden kannwird der Text hinter alt ausgegeben. Diese Angabe ist allerdings optional. Um eineParameterubergabe an das Applet zu erreichen werden ¡param¿ Tags verwendet.Hierin steht unter

”name“ der Name des Parameters und hinter

”value“ der zu

ubergebende Wert. Dieser kann dann im Applet mit der getParameter Methodeabgefragt werden. Hierbei wird immer ein String zuruckgegeben oder

”null“ falls

kein Wert angegeben ist.

2.2 Applets 47

import javax . swing . ∗ ;import java . awt . ∗ ;

public class Parameter extends JApplet{public void i n i t ( ){

St r ing text = getParameter ( ” textParameter ” ) ;JLabel anze ige1 = new JLabel

( ”Textparameter : ”+text ) ;S t r ing zah l = getParameter ( ” zahlParameter ” ) ;I n t eg e r zahlWert=0;

i f ( zah l !=null ){try{

zahlWert=In t eg e r . pa r s e In t ( zah l ) ;}catch ( NumberFormatException e ) {}

}

JLabel anze ige2 = new JLabel( ”Zahlenparameter : ”+zahlWert ) ;

this . setLayout (new GridLayout ( 2 , 1 ) ) ;this . add ( anze ige1 ) ;this . add ( anze ige2 ) ;

}}

Um dieses Applet in eine HTML Seite einzubinden und Parameter zu ubergebenkann folgender HTML Code verwendet werden. Dass Applet befindet sich in derDatei

”Parameter.class“.

<applet code=Parameter . class width=”200” height=”200”><param name=” textParameter ” value=” Texts t r ing ”><param name=”zahlParameter ” value=”1000”>

</applet>

Da Applets sich sehr einfach in Webseiten einbinden lassen sind einige Sicher-heitsbeschrankungen eingefuhrt worden. Am offensichtlichsten ist die Bildunter-schrift in neuen Fenstern. Wenn aus einem Java Applet ein neuer Frame geoffnetwird ist am unteren Rand die Nachricht

”Java Applet Window“ zu lesen. Die-

se kann nur vom Benutzer selbst abgeschaltet werden. Damit soll z.B. verhindertwerden, dass Passwortabfragen nachgebildet werden und so Benutzerpasswortererfragt werden konnen.

2.2 Applets 48

import javax . swing . JApplet ;import javax . swing . ∗ ;

public class AppletFrame extends JApplet{public void i n i t ( ) {// E r s t e l l e n e ine s neuen Fens ters

JFrame f e n s t e r = new JFrame( ”Fenster aus dem Applet ! ” ) ;

f e n s t e r . s e tV i s i b l e ( true ) ;f e n s t e r . s e t S i z e ( 100 , 100 ) ;f e n s t e r . s e tLoca t i on (100 , 100 ) ;

//Ein Tex t f e l d in das Fenster e in f u genJLabel t ex t = new JLabel

( ”Hier kann e in ”+”normales Fenster s e i n ! ” ) ;f e n s t e r . getContentPane ( ) . add ( text ) ;

}}

Abbildung 2.13. Ansicht eines Frames aus einem JApplet

Eine weitere Einschrankung gibt es beim Lesen bzw. Schreiben auf das Dateisys-tem. Applets durfen nicht direkt auf das Dateisystem des Host-Rechners schreiben.Zusatzlich durfen Applets keine neuen Netzwerkverbindungen aufbauen, oder An-wendungen auf dem Host-Rechner starten.

2.3 GUI Elemente 49

2.3 GUI Elemente

Um eine Interaktion mit der Grafischen Benutzeroberflache zu verwirklichen wer-den so genannte GUI (Graphical User Interface) Elemente verwendet. Diese Be-dienelemente lassen sich in verschiedene Gruppen einteilen.

2.3.1 Buttons

Eine der wichtigsten Gruppen der Bedienelemente sind die Knopfe bzw. Buttons.Buttons werden fast immer eingesetzt wenn eine Eingabe bestatigt werden soll,oder um direkt eine Entscheidung zu treffen. Auch bei den Buttons gibt es mehrereverschiedene Varianten.

• “JButton“ Bei einem JButton handelt es sich um einen”normalen“ Button wie

er zum Betatigen von Eingaben oder auch bei Entscheidungen verwendet wird.•

”JToggleButton“ Ein ToggleButton ist am ehesten mit einem normalen Schalter

zu vergleichen. Wenn er betatigt wird bleibt er gedruckt bis er wieder ausge-schaltet wird.

•”JRadioButton“ Ein RadioButton wird meist zur Auswahl einer von mehreren

Optionen verwendet. Dabei ist es allerdings notig die verschiedenen RadioBut-tons zu einer Gruppe, einen so genannten

”ButtonGoup“ zusammenzufassen.

Erst dadurch wird verhindert, dass mehrere zusammengehorige RadioButtonsgleichzeitig aktiv werden konnen.

•”JCheckBox“ Wenn im Gegensatz zu RadioButtons auch mehr als eine Option

angewahlt werden kann werden meist CheckBoxes verwendet.

Diese verschiedenen Buttons haben einige Funktionen gemeinsam. Die wichtigstensind die Konstruktoren, entweder nur mit einem String fur den Namen, mit einemIcon, oder mit einem String und einem Icon. Die Auswahlbuttons,

”JToggleBut-

ton“,”JRadioButton“ und

”JCheckBox“ konnen zusatzlich noch einen

”boolean“

Wert haben, der angibt ob der jeweilige Button aktiviert ist. Eine weitere Methodeist

”doClick()“ die einen Programmgesteuerten Klick auf den Button auslost. Ne-

ben einem Namen im Konstruktor, der fur die Beschriftung sorgt, kann ein Buttonauch ein spezielles ActionCommand erhalten. Dieses kann bei der Ereignisbehand-lung eingesetzt werden. Wenn kein ActionCommand angegeben wird, wird der imKonstruktor angegeben String hierfur verwendet. Um eine Ereignisbehandlung zurealisieren, werden ActionListener mit

”addActionListener“ angehangt bzw. mit

”removeActionListener“ wieder entfernt. Bei den Auswahlbuttons gibt es zusatz-

lich die Funktionen”isSelected“ bzw.

”setSelected“ um den Zustand zu erfragen

bzw. zu setzen. In dem Beispiel sind alle Buttons bis auf den ToggleButton ver-wendet. Man kann uber die RadioButtons in der oberen linken Ecke einstellen obdie Zaher hoch bzw. runter gezahlt werden. Dadurch das sich die beiden RadioBut-tons in einer Buttongroup befinden kann immer nur einer der beiden RadioBut-tons ausgewahlt werden. Mit den beiden Checkboxes in der rechten oberen Eckekann ausgewahlt werden welcher Zahler verandert werden soll. Hierbei ist es auchmoglich keinen oder beide CheckBoxes zu aktivieren. Als auslosendes Element wird

2.3 GUI Elemente 50

Abbildung 2.14. Ansicht der verschiedenen Buttons

ein normaler Button in der linken unteren Ecke verwendet. Sobald dieser Buttonbetatigt wird, wird gemaß den oben getroffenen Einstellungen gezahlt.

//Die RadioButtons werden in e ine ButtonGroup g e l e g tButtonGroup bg = new ButtonGroup ( ) ;

//RadioButtons e r s t e l l l e nJRadioButton rb hoch = new JRadioButton

( ” Zah ler erhohen” ) ;rb hoch . addAct ionLis tener ( this ) ;rb hoch . s e t S e l e c t e d ( true ) ;JRadioButton rb runt e r = new JRadioButton

( ” Zah ler v e r r i ng e rn ” ) ;rb runt e r . addAct ionLis tener ( this ) ;bg . add ( rb hoch ) ;bg . add ( rb runt e r ) ;pane l rb . add ( rb hoch ) ;pane l rb . add ( rb runt e r ) ;panel . add ( pane l rb ) ;

//Panel f u r d i e CheckBoxes e r s t e l l e nJPanel pane l cb = new JPanel ( ) ;pane l cb . setLayout (new GridLayout ( 2 , 1 ) ) ;

//CheckBoxes e r s t e l l e nc b l i n k s = new JCheckBox ( ”Linker Z ah ler ” ) ;c b r e ch t s = new JCheckBox ( ”Rechter Z ah ler ” ) ;pane l cb . add ( c b l i n k s ) ;pane l cb . add ( cb r e ch t s ) ;panel . add ( pane l cb ) ;

// Labe l s f u r d i e Textausgabe e r s t e l l e nl a b e l l i n k s = new JLabel

( ” Linker Z ah ler : ”+ c t r l i n k s ) ;panel . add ( l a b e l l i n k s ) ;l a b e l r e c h t s = new JLabel

2.3 GUI Elemente 51

( ”Rechter Z ah ler : ”+ c t r r e c h t s ) ;panel . add ( l a b e l r e c h t s ) ;

//Button zum Zahlen e r s t e l l e nJButton button = new JButton ( ”Druck mich ! ” ) ;button . addAct ionLis tener ( this ) ;panel . add ( button ) ;

Im Quelltext kann man sehen wie die einzelnen Buttons erstellt werden, zusatzlichwerden die beiden RadioButtons in einer ButtonGroup zusammengefasst. Außer-dem wurde nur den RadioButtons und dem Button ein Actionlistener zugewiesen.Das zeigt dass man jedem Button einen Actionlistener anhangen kann aber nichtmuss.

public void act ionPerformed ( ActionEvent evt ) {// Beschr i f t ung des Aus l o s er s ab f ragen

St r ing s=evt . getActionCommand ( ) ;// Fa l l s RadioButton ”Zah ler erhohen”

i f ( s==”Zah ler erhohen” )z ah len =1;

// Fa l l s RadioButton ”Zah ler ve r r inge rn ”else i f ( s==”Zah ler v e r r i ng e rn ” )

z ah len=−1;// Sonst muss es der Button ”Druck mich !” se in

else {//CheckBox Links ab f ragen ob a k t i v i e r t

i f ( c b l i n k s . i s S e l e c t e d ( ) )c t r l i n k s+=zah len ;

//CheckBox Rechts ab f ragen ob a k t i v i e r ti f ( cb r e ch t s . i s S e l e c t e d ( ) )

c t r r e c h t s+=zah len ;}

// Beschr i f t ung a k t u a l i s i e r e nl a b e l l i n k s . setText ( ”Linker Z ah ler : ”+c t r l i n k s ) ;l a b e l r e c h t s . setText ( ”Rechter Z ah ler : ”+c t r r e c h t s ) ;

}

In der”actionPerformed“ Methode wird zwischen den zwei RadioButtons und

dem Button anhand der Beschriftung unterschieden. Wenn einer der RadioButtonsbetatigt wurde wird die Zahlvariable entsprechend verandert. Wenn der Buttonzum Zahlen betatigt wurde, wird erst abgefragt welche CheckBox aktiviert ist.Der entsprechende Zahler wird dann mit der Zahlvariable erhoht oder verringert.

2.3 GUI Elemente 52

2.3.2 Textelemente

Um in einer grafischen Benutzerumgebung textuelle Eingaben in das System zutatigen sind Textelemente zwingend notwendig. Auch um Ausgaben vom Pro-gramm zu erhalten werden Textelemente benotigt.

Labels

Zur Textausgabe werden oft Labels eingesetzt Diese sind nur als Text in der Be-nutzeroberflache zu erkennen. Der Benutzer hat keinerlei Moglichkeit den Text zuverandern oder zu beeinflussen. Labels haben Konstruktoren mit String, Icon oderString und Icon. Außerdem gibt es Methoden zum auslesen und verandern desTextes,

”getText“ und

”setText“. Des Weiteren gibt es Methoden zum verandern

des Aussehens.

TextField

Um dem Benutzer auch eine Moglichkeit zu geben kleine Eingaben zu machen wer-den JTextFields eingesetzt. Diese haben Konstruktoren mit einem String, Integeroder beidem. Der String gibt dabei den Inhalt an und der Integer Wert die Spal-tenanzahl. Hier gibt es Methoden zum auslesen und verandern des Textes. Außer-dem konnen Textfelder ausgeschaltet werden, so dass Sie nur noch Text anzeigenkonnen. Auch an Textfelder konnen ActionListener angehangen werden. Ablei-tungen des JTextField sind JFormattedTextField und JPasswortField. In einemJFormattedTextField kann eine Formatierung fur den Text mitgegeben werden,z.B. fur Datum oder Zeit eingaben. Ein JPasswordField versteckt den eingegebe-nen Text hinter Echo-Zeichen.

Abbildung 2.15. SicherePassworteingabe

// Pas swor t f e l d erzeugenJPasswordField pw = new JPasswordField ( ) ;pw . addAct ionLis tener ( this ) ;panel . add (pw ) ;

// Labe l zur Ausgabe erzeugenlb = new JLabel ( ”Passwort : ” ) ;panel . add ( lb ) ;

Bei der Erzeugung von Passwort Felder gibt es keine Besonderheiten.

2.3 GUI Elemente 53

//Ein ActionEvent kann nur von dem Passwor t f e l d kommenJPasswordField pw = ( JPasswordField ) evt . getSource ( ) ;

//Das Pas swor t f e l d kann nur char [ ] zur uckgebenchar [ ] pass = pw. getPassword ( ) ;

//Das char [ ] muss e x p l i z i t in einen S t r ing umgewandelt werdenlb . setText ( ”Passwort : ”+new St r ing ( pass ) ) ;

Bei der Abfrage des Textes kann jedoch nur ein Character Feld zuruckgegebenwerden. Dieses muss dann explizit in einen String umgewandelt werden.

TextArea

Um großere Texte eingeben zu konnen werden TextAreas benotigt. Hierbei handeltes sich um mehrzeilige Eingabefenster fur Text. Der Konstruktor fur eine JTextA-rea kann leer sein, einen String, zwei Integer oder einen String und zwei IntegerArgumente erhalten. Der String gibt dabei den Text in der Area an. Die IntegerArgumente geben die Zeilen und Spaltenanzahl an. Weitere Methoden sind

”ge-

tRows“,”getColumns“ bzw.

”setRows“,

”setColumns“ um die Zeilen- bzw. Spal-

tenanzahl auszulesen bzw. zu setzen. Zusatzlich gibt es eine”insert“ Methode mit

einem String und einem Integer Argument zum einfugen eines Textes an einer be-stimmten Stelle. Mit der Methode

”append“ kann ein String am Ende angehangen

werden. Zusatzlich kann mit”replaceRange“ ein Bereich des Textes uberschrieben

werden. Dazu werden ein String und je ein Integer fur Start- und Endpositionbenotigt.

2.3.3 Slider

Ein weiteres Eingabeelement ist ein Schieberegler, der JSlider. Dabei handelt essich um eine grafische Moglichkeit Zahlenwerte einzustellen. Es gibt einen Kon-struktor ohne Argumente, mit Werten fur Minimum und Maximum, einen Kon-struktor mit Minimum, Maximum und Anfangswert, und einen mit zusatzlicherAngabe der Ausrichtung. Jeder dieser Werte kann auch uber eine Methode imNachhinein ausgelesen bzw. neu gesetzt werden. Mit Hilfe weiterer Methodenkann der Abstand zwischen den Markierungen eingestellt werden. Dabei unter-scheidet man zwischen einem MinorTickSpacing, fur die genauen Abstande undeinem MajorTickSpacing fur eine grobe Unterteilung. Diese Markierungen konnenmit

”setPaintTicks“ aktiviert werden. Mittels

”getPaintTicks“ kann bestimmt wer-

den ob die Markierungen angezeigt werden. Weiter gibt es die Moglichkeit mittels

”setPaintLabels“ bzw.

”getPaintLabels“ zu bestimmen ob eine Beschriftung ange-

zeigt werden soll. Einem JSlider kann ein ChangeListener angehangen werden umVeranderungen abzufragen.

2.3.4 ComboBox

Die JComboBox dient zur Auswahl eines Elementes aus einer Liste. In einer Com-boBox wird immer nur das aktuell ausgewahlte Element angezeigt. Bei den Ele-

2.3 GUI Elemente 54

menten in einer ComboBox kann es sich um beliebige Objekte handeln. Es gibtKonstruktoren ohne Argumente, mit einem Objekt Feld oder einem Vektor. Ele-mente konnen mit

”addItem“ zu der ComboBox hinzugefugt werden konnen. Um

Elemente zu entfernen gibt es die Moglichkeit ein Element mit”removeElement“,

”removeElementAt“ oder mit

”removeAllElements“ alle Elemente aus der Combo-

Box zu entfernen. Mit den Methoden”getSelectedItem“ bzw.

”getSelectedIndex“

kann man sich das gewahlte Objekte bzw. den Index des Objektes zuruckgebenlassen. Aquivalent dazu gibt es die Methoden

”setSelectedItem“ bzw.

”setSelected-

Index“ um ein Objekt auszuwahlen. An eine ComboBox kann ein ActionListeneroder ein ItemListener angehangen werden.

Abbildung 2.16. Slider und ComboBox

// S l i d e r e r s t e l l e ns ch i eb e r = new JS l i d e r ( 0 , 2 00 , 75 ) ;s ch i eb e r . s e tPa intT icks ( true ) ;s c h i eb e r . setMajorTickSpacing ( 1 0 ) ;s ch i eb e r . s e tOr i en t a t i on ( JS l i d e r .VERTICAL) ;s ch i eb e r . addChangeListener ( this ) ;l i n k s . add ( s ch i eb e r ) ;

//Anzeige e r r s t e l l e nanze ige = new JLabel ( ) ;anze ige . setText ( ””+s ch i eb e r . getValue ( ) ) ;l i n k s . add ( anze ige ) ;panel . add ( l i n k s ) ;

//ComboBox zur Auswahl e r s t e l l e nSt r ing [ ] e lemente = {”km −> m” , ”m −> km” ,

”kW −> PS” , ”PS −> kW” } ;cb = new JComboBox( elemente ) ;cb . s e tP r e f e r r e dS i z e (new Dimension ( 1 0 0 , 2 0 ) ) ;

2.3 GUI Elemente 55

cb . addAct ionLis tener ( this ) ;panel . add ( cb ) ;

// Tex t f e l d e r s t e l l e nt f = new JTextFie ld ( ””+Math . f l o o r ( ( 75∗1 . 6 )∗100 ) /100 ) ;t f . s e tEd i t ab l e ( fa l se ) ;t f . s e tP r e f e r r e dS i z e (new Dimension (50 , 2 0 ) ) ;panel . add ( t f ) ;

In diesem Beispiel wird ein Slider zur Dateneingabe verwendet. Um besser kon-trollieren zu konnen welcher Wert gerade eingestellt ist, wird dieser neben demSlider in einem Label angezeigt. Die ComboBox zur Auswahl der Umrechnungsartwird mit einem Feld von Stringelementen gefullt. Die Anzeige des Berechnungser-gebnisses wird in einer TextBox realisiert. Diese wurde mit

”setEditable(false)“ so

eingestellt das der Benutzer keine Eingaben darin vornehmen darf.

public void stateChanged (ChangeEvent evt ) {//Ein StateChanged Event kann nur vom S l i d e r kommen

JS l i d e r s ch i eb e r = ( JS l i d e r ) evt . getSource ( ) ;//Welche Umrechnung i s t in der ComboBox gew ah l t ?

St r ing auswahl = ( St r ing ) cb . ge tSe l e c t ed I t em ( ) ;//Die Anzeige des Sch i e be rwer t e s neu s e t z en

anze ige . setText ( ””+s ch i eb e r . getValue ( ) ) ;double ausgabe ;

// J ewe i l i g e Berechnung durchf uhreni f ( auswahl==”km −> m”)

ausgabe=Math . f l o o r( ( s ch i eb e r . getValue ( )∗1 . 6 )∗100 ) /100 ;

else i f ( auswahl==”m −> km” )ausgabe=Math . f l o o r

( ( s ch i eb e r . getValue ( )∗0 . 625 )∗100 )/100 ;else i f ( auswahl==”kW −> PS” )

ausgabe=Math . f l o o r( ( s ch i eb e r . getValue ( )∗1 . 36 )∗100 )/100 ;

elseausgabe=Math . f l o o r

( ( s ch i eb e r . getValue ( )∗0 . 735 )∗100 )/100 ;//Ausgabe im Tex t f e l d neu s e t z en

t f . setText ( ””+ausgabe ) ;}In der

”stateChanged“ Methode des Slider wird der aktuelle Wert des Sliders

abgefragt und in das Label eingetragen. Danach wird die Berechnung anhand dergewahlten Umrechnung durchgefuhrt. Das Ergebnis wird anschließend in das Text-feld eingetragen.Die

”actionPerformed“ Methode die der ActionListener der ComboBox implemen-

tiert ist dieser sehr ahnlich. Hierbei wird nur der Wert des Slider nicht aktualisiert.

2.3 GUI Elemente 56

Diese Methode ist dennoch notig um bei einer Veranderung der Umrechnung im-mer noch das aktuelle Ergebnis anzuzeigen.

2.3.5 Menus

Die Menuzeile in einem Java Programm kann aus mehreren Bestandteilen be-stehen. Der Grundstein hierfur ist immer eine JMenuBar. Ein Frame kann ge-nau eine JMenuBar besitzen. In dieser MenuBar konnen dann wiederum mehrereJMenu-Eintrage vorhanden sein. Jedes dieser JMenu bildet einen Menupunkt inder Menuzeile. Ein JMenu kann als Elemente JMenuItems oder wieder JMenusbeinhalten. Ein JMenu in einem JMenu realisiert ein Untermenu. Ein JMenuItemist ein normaler Menupunkt. Neben normalen MenuItems gibt es auch JCheckBox-MenuItems und JRadioButtonMenuItems. Diese MenuItems werden ahnlich wiedie entsprechenden Buttons behandelt. Auch hier gibt es MenuItems mit Text,Icon oder beidem. An jedes dieser MenuItems kann ein ActionListener angehan-gen werden. Zusatzlich gibt es bei Menueintragen noch die Moglichkeit dieser uberTastatureingaben zu steuern. Diese werden mit

”setMnemonic“ gesetzt.

2.3.6 Listen

Listen werden verwendet wenn man aus einer Anzahl von Elementen eines odermehrere Auswahlen muss. Ahnlich wie bei ComboBoxes handelt es sich bei den Ele-menten einer Liste auch hier um Objekte. Es gibt Konstruktoren ohne Argumente,mit einem Objekt Feld , einem Vektor oder einem ListModel. Ein ListModel mussdie Schnittstelle ListModel implementieren. Es gibt aber auch ein vorgefertigtesListModel, das DefaultListModel. Bei einer JList gibt es drei verschiedene Einstel-lungen fur verschiedene Selektionsmodi. Mit

”Single Selection“ kann immer nur ein

Element ausgewahlt werden. Bei”Single Interval Selection“ konnen mehrere zu-

sammenhangende Elemente ausgewahlt werden. Bei”Multiple Interval Selection“

konnen beliebige Elemente ausgewahlt werden. Die verschiedenen Selektionsmodikonnen mit

”setSelectionMode“ gesetzt werden. Um die selektierten Objekte zu

erkennen gibt es die Methoden”getSelectedIndex“ bzw.

”getSelectedIndices“, hier-

bei werden die Indizes der selektierten Objekte zuruckgegeben. Außerdem gibt esauch noch die Methoden

”getSelectedValue“ bzw.

”getSelectedValues“, hier wer-

den die Objekte direkt zuruckgegeben. Um die Selektionen zu setzen konnen dieMethoden

”setSelectedIndex“,

”setSelectedIndices“,

”setSelectedValue“ und

”se-

SelectedIntervall“ verwendet werden. An eine Liste konnen ListSelectionListenerangehangen werden.

// Li s t en erzeugenDefaultListModel modelLinks = new DefaultListModel ( ) ;modelLinks . addElement ( ”Links 1” ) ;modelLinks . addElement ( ”Links 2” ) ;modelLinks . addElement ( ”Links 3” ) ;modelLinks . addElement ( ”Links 4” ) ;

2.3 GUI Elemente 57

Abbildung 2.17. Menus und Listen

DefaultListModel modelRechts = new DefaultListModel ( ) ;modelRechts . addElement ( ”Rechts 1” ) ;modelRechts . addElement ( ”Rechts 2” ) ;modelRechts . addElement ( ”Rechts 3” ) ;modelRechts . addElement ( ”Rechts 4” ) ;

l i n k s = new JL i s t ( modelLinks ) ;r e ch t s = new JL i s t ( modelRechts ) ;

panel . add ( l i n k s ) ;panel . add ( r e ch t s ) ;

//Menu erzeugenJMenuBar menuBar = new JMenuBar ( ) ;JMenu menu , submenu ;JMenuItem item ;JRadioButtonMenuItem rbitem ;

//Hauptmenupunkt Verschiebmenu = new JMenu( ”Verschieben ” ) ;

//Untermenu S tu f e 1submenu = new JMenu( ”Links −> Rechts ” ) ;

//Menuauswahlitem = new JMenuItem( ” Al l e ” ) ;item . setActionCommand ( ”Links Al l e ” ) ;item . addAct ionLis tener ( this ) ;submenu . add ( item ) ;

//Menuauswahl

2.3 GUI Elemente 58

item=new JMenuItem( ” S e l e k t i e r t e ” ) ;item . setActionCommand ( ”Links S e l e k t i e r t e ” ) ;submenu . add ( item ) ;item . addAct ionLis tener ( this ) ;menu . add ( submenu ) ;

//Untermenu S tu f e1submenu = new JMenu( ”Rechts −> Links ” ) ;item = new JMenuItem( ” Al l e ” ) ;...

//Hauptmenu Se l e k t i on sau swah lmenu = new JMenu( ” Selekt ionsmodus ” ) ;ButtonGroup bg = new ButtonGroup ( ) ;

//Menuauswahlrbitem=new JRadioButtonMenuItem ( ” S ing l e S e l e c t i o n ” ) ;rbitem . addAct ionLis tener ( this ) ;rbitem . doCl ick ( ) ;bg . add ( rbitem ) ;menu . add ( rbitem ) ;...menuBar . add (menu ) ;f e n s t e r . setJMenuBar (menuBar ) ;

In diesem Beispiel gibt es zwei Listen. Eine links und eine rechts. Diese arbeitenjeweils mit einem DefaultListModel mit passenden Eintragen. Zusatzlich gibt esein Menu mit dem Elemente aus der einen Liste in die andere verschoben werdenkonnen. Zusatzlich gibt es die Moglichkeit den Selektionsmodus zu verandern.

public void act ionPerformed ( ActionEvent evt ){// Se lek t ionsmodus s e t z en

i f ( evt . getActionCommand()==” S ing l e S e l e c t i o n ” ) {

l i n k s . se tSe lec t ionMode( L i s tSe l e c t i onMode l . SINGLE SELECTION) ;

r e ch t s . se tSe lect ionMode( L i s tSe l e c t i onMode l . SINGLE SELECTION) ;

}// Se lek t ionsmodus s e t z en

else i f ( evt . getActionCommand()==” S ing l e I n t e r v a l S e l e c t i o n ” ) {

l i n k s . se tSe lec t ionMode( L i s tSe l e c t i onMode l . SINGLE INTERVAL SELECTION) ;

r e ch t s . se tSe lect ionMode

2.3 GUI Elemente 59

( L i s tSe l e c t i onMode l . SINGLE INTERVAL SELECTION) ;}

// Se lek t ionsmodus s e t z enelse i f ( evt . getActionCommand()==

” Mult ip l e I n t e r v a l S e l e c t i o n ” ) {l i n k s . se tSe lec t ionMode

( L i s tSe l e c t i onMode l .MULTIPLE INTERVAL SELECTION) ;r e ch t s . se tSe lect ionMode

( L i s tSe l e c t i onMode l .MULTIPLE INTERVAL SELECTION) ;}

// A l l e Elemente von Links nach Rechts v e r s ch i e b enelse i f ( evt . getActionCommand()==”Links Al l e ” ){//Auslesen der verwendeten L i s t enmode l l e

DefaultListModel lm =( DefaultLis tModel ) l i n k s . getModel ( ) ;

DefaultListModel rm =( DefaultLis tModel ) r e ch t s . getModel ( ) ;

//Bestimmen der Anzahl der Elemente der l i n k en L i s t eint anzahl = lm . g e tS i z e ( ) ;

// A l l e Elemente Links en t f e rnen und Rechts h inzu f u genfor ( int i =0; i<anzahl ; i++) {

Object elem = lm . get ( 0 ) ;lm . remove ( 0 ) ;rm . addElement ( elem ) ;

}//Die L i s t enmode l l e neu s e t z en

l i n k s . setModel ( lm ) ;r e ch t s . setModel (rm ) ;

}// S e l e k t i e r t e Elemente von Links nach Rechts v e r s ch i e b en

else i f ( evt . getActionCommand()==”Links S e l e k t i e r t e ” ){//Auslesen der verwendeten L i s t enmode l l e

DefaultListModel lm =( DefaultLis tModel ) l i n k s . getModel ( ) ;

DefaultListModel rm =( DefaultLis tModel ) r e ch t s . getModel ( ) ;

//Die S e l e k t i e r t e n Objek te aus l e s enObject [ ] mark iert = l i n k s . g e tSe l e c t edVa lue s ( ) ;

//Die S e l e k t i e r t e n Elemente Links en t f e rnen und Rechts e in f u genfor ( int i =0; i<markiert . l ength ; i++) {

lm . removeElement ( markiert [ i ] ) ;rm . addElement ( markiert [ i ] ) ;

}//Die L i s t enmode l l e neu s e t z en

l i n k s . setModel ( lm ) ;

2.3 GUI Elemente 60

r e ch t s . setModel (rm ) ;}

// A l l e Elemente von Rechts nach Links v e r s ch i e b enelse i f ( evt . getActionCommand()==”Rechts Al l e ” ){

. . .}

// S e l e k t i e r t e Objek te von Rechts nach Links v e r s ch i e b enelse i f ( evt . getActionCommand()==”Rechts S e l e k t i e r t e ” ){

. . .}

}

In dem ActionListener wird anhand der ActionCommands der auszufuhrende Be-fehl unterschieden. In den ersten drei Fallen wurde ein anderer Selektionsmodusausgewahlt. Hier wird dann bei beiden Listen der Modus gewechselt. Im nachs-ten Fall werden alle Elemente von Links nach Rechts verschoben. Dazu werden dieListenmodelle der beiden Listen ausgelesen. Die Anzahl der Elemente in der linkenListe wird bestimmt. Daraufhin werden alle Elemente aus dem linken Listenmo-dell entfernt und rechts eingefugt. Am Ende werden die beiden Listenmodelle neugesetzt. In die Umgekehrte Richtung ist die Vorgehensweise exakt die gleiche. Derandere Fall ist, wenn nur die selektierten Elemente verschoben werden sollen. Da-bei wird nicht die Anzahl aller Elemente ermittelt, sonder nur die der selektierten.Diese werden dann verschoben.

2.3.7 Baume

Baume ermoglichen es dem Benutzer ein Element auszuwahlen, dabei sind die Ele-mente in einer Baumstruktur angeordnet. Das ermoglicht es dem Benutzer einzelneAste auf- und zuzuklappen. Dadurch kann ein große Anzahl an Elementen uber-sichtlich dargestellt werden. Die wichtigsten Konstruktoren konnen ein Baummo-dell, das TreeModel, einen Knoten der die Schnittstelle TreeNode implementiert,ein Object Feld oder einen Vektor erhalten. Die Elemente des Objekt Feldes bzw.des Vektors werden als neue Elemente direkt an die Wurzel angehangt. Die Wurzelwird dabei nicht angezeigt.Die Methoden

”getSelectionPath“ bzw.

”setSelectionPath“ ermoglichen es, dass

gewahlte Element zu lesen bzw. zu setzen. Eine weitere wichtige Methode ist”ex-

pandPath“ um einen Ast des Baumes aufzuklappen.An einen Baum kann ein TreeSelectionListener, TreeExpandListener oder ein Tree-WillExpandListener angehangen werden. Alle diese Listener werden zu verschiede-nen Zeitpunkten aktiv. Der TreeSelectionListener wird aktiv wenn ein Knoten se-lektiert wird. Der TreeExpandListener wird aktiv nachdem ein Knoten aufgeklapptwird. Der TreeWillExpandListener wird aktiv, wenn ein Knoten aufgeklappt wer-den soll.

//Knoten e r s t e l l e nDefaultMutableTreeNode top =

2.3 GUI Elemente 61

Abbildung 2.18. Ein Baum

new DefaultMutableTreeNode ( ”Autos” ) ;DefaultMutableTreeNode node ;DefaultMutableTreeNode wagen ;//Neuen Knoten 1 . S tu f e erzeugennode = new DefaultMutableTreeNode ( ”Audi” ) ;

//Knoten 2 . Stu fen erzeugenwagen = new DefaultMutableTreeNode ( ”A3” ) ;node . add (wagen ) ;wagen = new DefaultMutableTreeNode ( ”A4” ) ;node . add (wagen ) ;wagen = new DefaultMutableTreeNode ( ”A6” ) ;node . add (wagen ) ;wagen = new DefaultMutableTreeNode ( ”A8” ) ;node . add (wagen ) ;

//Den Knoten 1 . S tu f e an den Wurzelknoten anhangentop . add ( node ) ;

.

.

.

//Den Baum erzeugen und im Panel e in f u genJTree baum = new JTree ( top ) ;

// L i s t ene r an den Baum anhangenbaum . addTreeSe l e c t i onL i s t ene r ( this ) ;panel . add (baum ) ;

2.3 GUI Elemente 62

//Anzeige des ausgewah l ten Zweigesanze ige = new JLabel ( ”Ausgewahlt : ” ) ;panel . add ( anze ige , BorderLayout .SOUTH) ;

In diesem Beispiel werden die Daten mittels eines Wurzelknoten eingegeben. Dazuwird der vorgefertigte DefaultMutableTreeNode verwendet. Dieser implementiertdie TreeNode Schnittstelle. Hier wird ein Wurzelknoten

”Auto“ angelegt. Dieser be-

kommt als Blatter verschiedene Automobilhersteller angehangen. Diese wiederumbekommen als Blatter die verschiedenen Typen. Als Ausgabe wird der selektiertePfad ausgegeben.

2.3.8 weitere GUI Elemente

Neben den hier vorgestellten GUI Elementen gibt es noch einige weitere. Dazugehoren z.B. Tabellen, Farb- oder Dateiwahler. Informationen zu diesen Elementenund auch genauere Informationen zu den hier vorgestellten GUI Elementen sindin der Java API zu finden. Diese ist unter [Javc] zu erreichen.

3

Grundlagen C#

Da das .Net-Framework von Microsoft in vielen Bereichen der Softwareentwick-lung eine große Rolle spielt, sollen die in den vorherigen Kapiteln besprochenenGrundlagen im Folgenden auf C# ubertragen werden.

Im ersten Kapitel der C#-Einfuhrung werden die Grundlagen zur Erzeugungvon Fenstern erlautert.

3.1 Erstellung eines Projekts in Visual Studio

Da C#-Programme normalerweise im Visual Studio entwickelt werden, werden andieser Stelle die notigen Einstellungen zur Erstellung eines Projekts beschrieben.

Der Dialog (siehe Abb. 4.1) zur Erzeugung eines neuen Projekts sollte selbst-erklarend sein. Es muß lediglich ein leeres Projekt aus dem Ordner

”Visual C#-

Projekte“ ausgewahlt und ein Name und ein Speicherort angegeben werden.

3.1 Erstellung eines Projekts in Visual Studio 64

Abbildung 3.1. Neues Projekt

3.1.1 Verweise

Anmerkung: in neueren Versionen von Visual Studio (z.B. 2005) werden Verweiseautomatisch erzeugt, wenn entsprechende using-Anweisungen verwendet werden.Der folgende Abschnitt betrifft daher hauptsachlich Anwender von Visual Studio2003.

Da die Funktionen von C# in verschiedenen DLLs abgelegt sind, mussen so-genannte Verweise angelegt werden, um Zugriff darauf zu erhalten. In diesemKapitel werden drei Verweise benotigt: System, System.Drawing und Sys-tem.Windows.Forms. Der Dialog zum Hinzufugen von Verweisen offnet sichdurch Rechtsklick auf

”Verweise“,

”Verweise hinzufugen...“.

3.2 Fenster und GUI-Elemente 65

Abbildung 3.2. Verweise

3.2 Fenster und GUI-Elemente

3.2.1 Beispiel 1 - ein einfaches Fenster in C#

Als erster Schritt zur Grafikprogrammierung mit C# soll im folgenden Kapitel dieErzeugung und Steuerung von Fenstern und GUI-Elementen erlautert werden. Daserste Beispielprogramm erzeugt ein leeres Fenster (in C# werden Fenster als Formsbezeichnet) mit einer vordefinierten Große. Zu Beginn erstellen wir einige using-Direktiven, damit die verwendeten C#-Klassen nicht jedes Mal mit vollstandigemNamespace angegeben werden mussen.

us ing System ;us ing System . Windows . Forms ;us ing System . Drawing ;

Der nachste Schritt ist die Implementierung der eigentlichen Klasse Frames01,die von der Klasse Form erbt. Im Konstruktor definieren wir die EigenschaftenText und Size der Klasse, die sie durch die Vererbung von Form geerbt hat. Die

3.2 Fenster und GUI-Elemente 66

Große des Fensters wird mit Hilfe eines Objekts der Klasse Size ubergeben. DieWerte geben die Breite und Hohe des Fensters in Pixeln an.

pub l i c c l a s s Frames01 : Form{

pub l i c Frames01 ( ){

t h i s . Text = ”C# − e i n f a ch e s Form” ;t h i s . S i z e = new S i z e (300 , 200 ) ;

}. . .

}

Als letzten Schritt implementieren wir die statische Methode Main(), die alsEinstiegspunkt in die Anwendung dient. Dabei muss auf die korrekte Groß- undKleinschreibung geachtet werden. Wir verwenden die Methode run() der C#-Klasse Application, um eine Standard-Anwendungsschleife mit dem ubergebenenObjekt zu starten. Anmerkung: dieser Aufruf startet keinen neuen Thread, es wirdalso in diesem Fall keine Nebenlaufigkeit erzeugt.

pub l i c stat ic void Main ( ){

Appl i cat ion .Run(new Frames01 ( ) ) ;}

Abbildung 3.3. Beispielprogramm frames01

3.2 Fenster und GUI-Elemente 67

3.2.2 Beispiel 2 - Buttons und EventHandler

In diesem Beispiel soll ein Button auf einer Form platziert werden. Außerdemwird ein EventHandler implementiert, der Klickereignisse des Buttons abfangt undauswertet. Das Programm baut auf dem im vorigen Kapitel erstellten Programmauf.

Die Klasse Frames02 enthalt nun zwei Referenzen: eine Referenz auf den Buttonund eine Referenz auf den zugehorigen Eventhandler.

pub l i c c l a s s Frames02 : Form{

pr i va t e Button myButton ;p r i va t e EventHandler newFormEvent ;. . .

Im Konstruktor der Klasse werden diese Referenzen auf neu erzeugte Ob-jekte der jeweiligen Klassen gesetzt. Die Eigenschaften des Buttons werden beider Erzeugung des Button-Objekts noch nicht gesetzt. Bei der Erzeugung desEventHandler-Objekts muss eine Methode angegeben werden, die aufgerufen wird,sobald das Event ausgelost wird. Die entsprechende Methode implementieren wirweiter unten im Kapitel.

pub l i c Frames02 ( ){newFormEvent = new EventHandler ( myButtonFunktion ) ;myButton = new Button ( ) ;. . .

Zunachst fullen wir die notigen Eigenschaften des Buttons; Neben der Position(myButton.Location), der Große (myButton.Size) und dem Text (myButton.Text)verknupfen wir den Button an dieser Stelle mit dem erzeugten EventHandler. Dawir nur an Klickereignissen des Buttons interessiert sind, verkupfen wir lediglichmyButton.Click. Die Verknupfung ist in C# mit dem bekannten

”+=“-Operator

moglich. Abschließend mussen wir den Button zu der C#-Collection Controls hin-zufugen, in der alle Controls des Forms enthalten sind. GUI-Elemente werden nurangezeigt, wenn sie zur Controls-Collection hinzugefugt wurden.

. . .myButton . Text = ”myButton” ;myButton . Locat ion = new Point (75 , 5 0 ) ;myButton . S i z e = new S i z e (150 , 3 0 ) ;

myButton . Cl i ck += newFormEvent ;

Contro l s .Add(myButton ) ;. . .

Als letzter Schritt muss die EventHandler-Methode implementiert werden, diebei der Erzeugung des EventHandlers angegeben wurde. Der Kopf von EventHandler-

3.2 Fenster und GUI-Elemente 68

Methoden muss immer zwei Parameter enthalten: die Quelle des Events (Typ Ob-ject) und optionale Argumente (Typ EventArgs). Die unten aufgefuhrte Methodefuhrt eine Prufung der Eventquelle durch, die in diesem kleinen Beispiel naturlicheigentlich uberflussig ist, da es nur eine mogliche Quelle gibt. Wurde die Formmehrere Buttons enthalten, konnte so eine Eventmethode je nach angeklicktemButton unterschiedliche Routinen ausfuhren. In diesem Fall wird lediglich eineMessageBox mit einer kurzen Meldung erzeugt, um zu zeigen, daß der Button wiegewunscht funktioniert.

p r i va t e void myButtonFunktion ( Object source , EventArgs e ){i f ( source == myButton ) {MessageBox . Show( ” Herz l i chen . . . ” ) ;

}}

Abbildung 3.4. Anwendung Frames02

3.2 Fenster und GUI-Elemente 69

3.2.3 Beispiel 3 - Labels

Im nachsten Beispiel werden Labels naher betrachtet. Labels sind einfache Con-trols, die aber eine wichtige Rolle bei der Informationsubermittlung an den Be-nutzer spielen. Im Gegensatz zu Textboxen konnen Labels nicht interaktiv vomBenutzer verandert werden. Die Erzeugung von Labels ist identisch zur Erzeugungvon Buttons (siehe vorheriges Kapitel). Ein Label hat eine bestimmte Große, einePosition und enthalt einen Text. Der folgende Codeauszug zeigt die Initialisierungeines einfachen Labels.

pub l i c c l a s s Frames03 : Form{

pr i va t e Label welcomeLabel ;p r i va t e Label i n f oLabe l ;

pub l i c Frames03 ( ){

t h i s . Text = ”C# − Labels ” ;t h i s . S i z e = new S i z e (300 , 500 ) ;

welcomeLabel = new Label ( ) ;welcomeLabel . S i z e = new S i z e (100 , 2 0 ) ;welcomeLabel . Locat ion = new Point (50 , 5 0 ) ;welcomeLabel . Text = ”Willkommen ! ” ;. . .

Das zweite Label wird im Folgenden initialisiert. Dabei wird zusatzlich zu denoben bereits gezeigten Eigenschaften das Border-Attribut gesetzt. Durch vorge-gebene C#-Variablen konnen verschiedene Rahmendesigns ausgewahlt werden. Indiesem Fall entscheiden wir uns fur einen Rahmen mit 3D-Optik. Anhand desRahmens laßt sich auch gut erkennen, daß der Text am Rand des Labels passendumgebrochen wird.

i n f oLabe l = new Label ( ) ;i n f oLabe l . S i z e = new S i z e (150 , 200 ) ;i n f oLabe l . Locat ion = new Point (50 , 100 ) ;i n f oLabe l . BorderSty le = System . Windows . Forms . BorderSty le . Fixed3D ;in f oLabe l . Text = ” S i e haben [ . . ] ” ;

Nach der Initialisierung mussen die Labels wieder wie im vorherigen Kapitelbeschrieben zur Controls-Collection hinzugefugt werden.

Contro l s .Add( welcomeLabel ) ;Contro l s .Add( in f oLabe l ) ;

3.2 Fenster und GUI-Elemente 70

Abbildung 3.5. Anwendung Frames03

3.2 Fenster und GUI-Elemente 71

3.2.4 Beispiel 4 - Maus-Events

Nach der Einfuhrung in grundlegende GUI-Controls soll in diesem Kapitel dieAbfrage von Maus-Events behandelt werden. Zur Ausgabe der Mausposition unddem Status der Maustasten dienen zwei Labels:

p r i va t e Label coordLabel ;p r i va t e Label k l i c kLabe l ;

Um interaktive Events abfangen zu konnen, muß wieder ein EventHandler er-stellt werden, der bei den entsprechenden Maus-Events eine Eventfunktion aufruft.Der Einfachheit halber erzeugen wir dazu in diesem Beispiel nur anonyme Event-Handler, da sie nur zur Verknupfung der Mausfunktionen mit der Eventfunktionbenotigt werden. Fur Maus-Events benotigen wir MouseEventHandler. In diesemBeispiel sollen mehrere Maus-Events abgefragt werden:

• MouseDown - Maustaste gedruckt• MouseMove - Mauscursor wird bewegt• MouseUp - Maustaste losgelassen

Dabei ist zu beachten, daß die Events nur ausgelost werden, wenn sich der Mauscur-sor uber der Form befindet und die Form aktiv ist. Die Eventfunktion mausEventwird spater im Kapitel naher erlautert.

t h i s .MouseDown += new MouseEventHandler ( t h i s . mausEvent ) ;t h i s . MouseMove += new MouseEventHandler ( t h i s . mausEvent ) ;t h i s . MouseUp += new MouseEventHandler ( t h i s . mausEvent ) ;

Die Eventfunktion mausEvent nimmt, wie fur Eventfunktionen ublich, zwei Para-meter an: die Quelle des Events und Eventargumente. Die Eventargumente ent-halten unter anderem Informationen uber gedruckte Maustasten und die Positiondes Mauscursors. Mit Hilfe der C#-Konstanten der Klasse MouseButtons laßt sichauf einfache Weise abfragen, ob und welche Maustaste gedruckt wurde.

p r i va t e void mausEvent ( ob j e c t sender , MouseEventArgs e ) {

switch ( e . Button ){case MouseButtons . Le f t :

k l i c kLabe l . Text = ” l i n k e Maustaste ” ;break ;

case MouseButtons . Right :

3.2 Fenster und GUI-Elemente 72

k l i c kLabe l . Text = ” recht e Maustaste ” ;break ;

case MouseButtons . Middle :k l i c kLabe l . Text = ” m i t t l e r e Maustaste ” ;break ;

default :k l i c kLabe l . Text = ” ke ine Taste gedr uckt ” ;break ;

}

//Koordinaten anze igencoordLabel . Text = ( e .X + ” : ” + e .Y) ;

}

Abbildung 3.6. Anwendung Frames04

3.2 Fenster und GUI-Elemente 73

3.2.5 Beispiel 5 - Web-Browser

C# bietet uber das WebBrowser-Control eine einfache Moglichkeit, um Webseitenin eigenen Anwendungen anzeigen zu konnen. In diesem Beispiel implementierenwir einen einfachen Webbrowser, um die Arbeitsweise dieses Controls zu verdeut-lichen. Das Programm soll aus zwei Fenstern bestehen: einem großen Fenster, dasden Webbrowser enthalt und ein kleines, in dem der Benutzer die gewunschte URLeingeben kann. Wir beginnen mit dem Hauptfenster:

c l a s s Frames05 : Form{

pr i va t e WebBrowser myWebBrowser ;p r i va t e TextBox urlBox = new TextBox ( ) ;

pub l i c Frames05 ( ){

t h i s . S i z e = new S i z e (800 , 800 ) ;myWebBrowser = new WebBrowser ( ) ;t h i s . Text = ”Web Browser” ;myWebBrowser . Dock = DockStyle . F i l l ;myWebBrowser . Locat ion = new Point (0 , 0 ) ;

Contro l s .Add(myWebBrowser ) ;myWebBrowser . Navigate ( ” http ://www. fh−t r i e r . de” ) ;. . .

}

Die Initialisierung des WebBrowser-Controls verlauft aquivalent zu den Con-trols aus den vorherigen Kapiteln. Da wir das gesamte Fenster fur die Ausgabe derWebseiten verwenden wollen, setzen wir das Attribut Dock des Controls auf denWert DockStyle.Fill. Damit wird sichergestellt, daß der Browser auch bei Verande-rung der Fenstergroße immer das gesamte Fenster ausfullt. Die Methode Naviga-te() dient zum Aufruf einer Webseite, wir laden in diesem Fall die Homepage derFachhochschule Trier. Um neue URLs an das WebBrowser-Control ubergeben zukonnen, erstellen wir die Methode

”loadURL()“:

pub l i c void loadURL( St r ing u r l ){myWebBrowser . Navigate ( u r l ) ;

}

Als nachsten Schritt implementieren wir die Main-Methode. Dabei muss auf eineBesonderheit geachtet werden: das WebBrowser-Control wird uber die windows-spezifische COM-Schnittstelle gesteuert. Die Kommunikation mit dem Control istin diesem Fall nur moglich, wenn die Anwendung

”single-threaded“ ablauft, also

keine Nebenlaufigkeiten enthalt. Um dies zu erreichen, muß das Attribut STA-Thread gesetzt werden, wie der folgende Codeausschnitt zeigt.

3.2 Fenster und GUI-Elemente 74

[ STAThread ]pub l i c stat ic void Main ( ){

Appl i cat ion .Run(new Frames05 ( ) ) ;}

Als letzter Schritt muss das kleine Fenster implementiert werden, mit dem dieSteuerung des Browsers ermoglicht werden soll. Dazu erstellen wir eine neue Klasse

”BrowserControl“, die von der Klasse Form erbt:

c l a s s BrowserControl : Form{TextBox urlBox = new TextBox ( ) ;Button loadButton = new Button ( ) ;Frames05 parent ;

pub l i c BrowserControl ( Frames05 parent ){

t h i s . parent = parent ;t h i s . TopMost = true ;. . .

Die Besonderheit hierbei ist, daß wir eine Referenz auf die Hauptform im Kon-struktor ubergeben. Dadurch haben wir Zugriff auf die Methode

”loadURL()“.

Durch das Setzen des Attributs”TopMost“ bleibt dieses Fenster immer im Vor-

dergrund. Der Button wird (wie in den vorherigen Kapiteln erlautert) mit einerEventfunktion verknupft. Diese ruft die o.g. Funktion

”loadURL()“ auf:

p r i va t e void buttonEvent ( ob j e c t sender , System . EventArgs e ){parent . loadURL( urlBox . Text ) ;

}

3.2 Fenster und GUI-Elemente 75

Abbildung 3.7. Anwendung Frames05

3.2.6 Beispiel 6 - Menu- und Statusbars

Ein weiteres wichtiges Werkzeug zur Gestaltung einer intuitiven GUI sind Menu-und Statusbars. Wahrend Menus dem Benutzer Zugriff auf wichtige Funktionendes Programms bieten, dienen Statusbars zur reinen Informationsvermittlung. DieImplementierung einer Statusbar funktioniert sehr ahnlich wie bei anderen GUI-Elementen. Da sie sich allerdings immer am unteren Rand des Fensters befindetund die gesamte Fensterbreite einnimmt, muß die Große und Position der Statusbarnicht initialisiert werden. Folgender Codeausschnitt zeigt die Initialisierung einerStatusbar.

3.2 Fenster und GUI-Elemente 76

c l a s s Frames06 : Form {pr i va t e StatusBar myStatusBar ;. . .myStatusBar = new StatusBar ( ) ;myStatusBar .Name = ”StatusBar ” ;myStatusBar . Text = ”Dies i s t d i e StatusBar ” ;myStatusBar . S i z ingGr ip = true ;

Contro l s .Add( myStatusBar ) ;. . .

Aktuelle Meldungen konnen je nach Programmzustand uber die Text-Eigenschaftan die Statusbar ubergeben werden.

Der Aufbau einer Menubar setzt sich aus zwei grundlegenden Elementen zu-sammen: Die Klasse MainMenu dient zur Erzeugung einer Menuleiste am oberenFensterrand. Alle enthaltenen Elemente des MainMenus sind Objekte der KlasseMenuItem. Visual Studio bietet die Moglichkeit, Menus uber einen Menudesigneranzulegen. In diesem Beispiel implementieren wir allerdings ein Menu von Hand,um den Aufbau von Menubars deutlich zu machen. Als Beispiel soll ein Menu miteinem Menupunkt

”Datei“ erstellt werden, der wiederum einige Untermenupunkte

enthalt. Dabei muss die Hierarchie der Menupunkte entsprechend aufgebaut wer-den. Als ersten Schritt legen wir alle benotigten Referenzen an. Wir benotigen einObjekt der Klasse MainMenu und jeweils ein Objekt fur jeden Menupunkt. Diefolgende Abbildung zeigt den Aufbau des zu implementierenden Menus.

Abbildung 3.8. Menuaufbau

c l a s s Frames06 : Form {. . .p r i va t e MainMenu myMainMenu ;

p r i va t e MenuItem myMItemDatei ;p r i va t e MenuItem myMItemOeffnen ;p r i va t e MenuItem myMItemSpeichern ;

3.2 Fenster und GUI-Elemente 77

pr i va t e MenuItem myMItemTrennlinie ;p r i va t e MenuItem myMItemOptionen ;p r i va t e MenuItem myMItemOption1 ;p r i va t e MenuItem myMItemOption2 ;. . .

Im Konstruktor erzeugen wir wie gewohnt die benotigten Objekte und weisen sieden Referenzen zu. Außerdem setzen wir die Text-Eigenschaften jedes Menuitems.

pub l i c Frames06 ( ) {. . .myMainMenu = new MainMenu ( ) ;

myMItemDatei = new MenuItem ( ) ;myMItemOeffnen = new MenuItem ( ) ;myMItemSpeichern = new MenuItem ( ) ;myMItemTrennlinie = new MenuItem ( ) ;

myMItemOptionen = new MenuItem ( ) ;myMItemOption1 = new MenuItem ( ) ;myMItemOption2 = new MenuItem ( ) ;

// Beschr i f tungenmyMItemDatei . Text = ”Datei ” ;

myMItemOeffnen . Text = ” Offnen” ;myMItemSpeichern . Text = ” Spe ichern ” ;myMItemTrennlinie . Text = ”−” ;myMItemOptionen . Text = ”Optionen” ;myMItemOption1 . Text = ”Option 1” ;myMItemOption2 . Text = ”Option 2” ;. . .

Da die MenuItems auf Mausklicks reagieren sollen, benotigen wir wie in denvorherigen Beispielen EventHandler, die bei Klickereignissen eine entsprechendeEventfunktion aufrufen. Wir verwenden wieder der Einfachheit halber anonymeObjekte der entsprechenden EventHandler-Klasse, da wir sie nur zum Verweis aufdie Eventfunktion myMenuEvent benotigen. Die Implementierung der Eventfunk-tion wird spater in diesem Kapitel erlautert.

. . .myMItemOeffnen . Cl i ck += new EventHandler (myMenuEvent ) ;myMItemSpeichern . C l i ck += new EventHandler (myMenuEvent ) ;myMItemOption1 . Cl i ck += new EventHandler (myMenuEvent ) ;myMItemOption2 . Cl i ck += new EventHandler (myMenuEvent ) ;. . .

Der nachste Schritt ist die Strukturierung des Menus in Hierarchieebenen. Je-des Objekt der Klassen MainMenu und MenuItem kann untergeordnete Objek-

3.2 Fenster und GUI-Elemente 78

te vom Typ MenuItem enthalten. Die Klassen enthalten jeweils eine CollectionMenuItems, in die untergeordnete Objekte mit der Methode Add() eingefugt wer-den konnen. Unser MainMenu soll das MenuItem

”Datei“ enthalten, das wieder-

um die ubrigen Menupunkte enthalt. Wir fugen also das MenuItem”Datei“ zur

MenuItems-Collection des MainMenus hinzu:

. . .myMainMenu . MenuItems .Add(myMItemDatei ) ;. . .

Die MenuItems bis auf Option 1 und Option 2 sollen direkt im”Datei“-Menu ent-

halten sein. Daher fugen wir sie der MenuItems-Collection des”Datei“-Menupunkts

hinzu.

. . .myMItemDatei . MenuItems .Add(myMItemOeffnen ) ;

myMItemDatei . MenuItems .Add( myMItemSpeichern ) ;myMItemDatei . MenuItems .Add( myMItemTrennlinie ) ;myMItemDatei . MenuItems .Add(myMItemOptionen ) ;. . .

Der Menupunkt”Optionen“ soll nun wiederum die beiden ubrigen Menupunkte

als Untermenu enthalten:

. . .myMItemOptionen . MenuItems .Add(myMItemOption1 ) ;myMItemOptionen . MenuItems .Add(myMItemOption2 ) ;

. . .

Das Objekt der Klasse MainMenu muss noch als Menu der Anwendung gesetztwerden. Das wird uber folgende Zuweisung erreicht (wobei sich

”this“ auf die

Klasse”Frames06“ bezieht, die von der C#-Klasse

”Form“ erbt.

. . .t h i s .Menu = myMainMenu ;. . .

3.2 Fenster und GUI-Elemente 79

Der letzte Schritt ist die Implementierung der Eventfunktion. In diesem Beispielwird der Text der Statusbar abhangig vom angeklickten Menupunkt gesetzt.

p r i va t e void myMenuEvent( ob j e c t sender , System . EventArgs e ) {

i f ( sender . Equals (myMItemOeffnen ) )

myStatusBar . Text = ” S i e haben den Menupunkt ’ Offnen ’ gewahlt . ” ;

else i f ( sender . Equals ( myMItemSpeichern ) )myStatusBar . Text = ” S i e haben den Menupunkt ’ Spe ichern ’ gewahlt . ” ;

else i f ( sender . Equals (myMItemOption1 ) )myStatusBar . Text = ” S i e haben den Menupunkt ’ Option 1 ’ gewahlt . ” ;

else i f ( sender . Equals (myMItemOption2 ) )myStatusBar . Text = ” S i e haben den Menupunkt ’ Option 2 ’ gewahlt . ” ;

}

3.2 Fenster und GUI-Elemente 80

3.2.7 Beispiel 7 - ComboBox

In diesem Beispielprogramm sollen die vielseitigen Moglichkeiten des ComboBox-Controls demonstriert werden. Dazu werden wir im Folgenden eine kleine Anwen-dung zur Verwaltung einer Adresskartei implementieren. Im ersten Schritt imple-mentieren wir dazu eine Klasse

”Adresse“, die zur Datenhaltung dienen soll. Jede

Adresse soll einen Vornamen, Nachnamen und eine Anschrift enthalten. Die Klassebesteht hauptsachlich aus einfachen get- und set-Funktionen, die an dieser Stellenicht naher erlautert werden, da sie recht leicht verstandlich sind. Wir betrachtenlediglich die ToString-Methode der Klasse:

pub l i c ov e r r i d e S t r ing ToString ( ){return nachname + ” , ” + vorname ;

}

Wichtig ist hierbei das Schlusselwort override: jedes Objekt in C# enthalt be-reits eine ToString-Methode, die einen standardisierten String mit Informationenuber das Objekt zuruckgibt. Comboboxen greifen auf diese Methode zu, um furjedes Element einen Menueintrag erstellen zu konnen. Da wir den Eintrag in derCombobox fur jede Adresse selbst definieren wollen, mussen wir die ToString-Methode uberschreiben. Um versehentliches Uberschreiben von Methoden zu ver-hindern, muß in C# das Schlusselwort override angegeben werden. Fehlt diesesSchlusselwort, meldet der Compiler einen Fehler.

Die Kartei-Anwendung enthalt drei Textfelder mit entsprechenden Labels, diesowohl zur Ausgabe der gespeicherten Adressen, als auch zur Eingabe von neuenAdressen oder zur Durchfuhrung von Anderungen dienen. Weiterhin erstellen wirdrei Buttons zur Steuerung der Anwendung: ein Button zur Erstellung einer neuenAdresse, ein Button zum Loschen der aktuell ausgewahlten Adresse und ein Buttonzur Speicherung von neuen Adressen.

c l a s s Frames07 : Form{

. . .p r i va t e Label vnLabel ; // Labe l ”Vorname :”pr i va t e Label nnLabel ; // Labe l ”Nachname :”pr i va t e Label adLabel ; // Labe l ”Adresse : ” ;

pr i va t e TextBox vnTextBox ; //Textbox Vornamepr i va t e TextBox nnTextBox ; //Textbox Nachnamepr i va t e TextBox adTextBox ; //Textbox Adresse

pr i va t e Button neuButton ; //Neueingabepr i va t e Button delButton ; // ak t . Adr . l o s chen

pr i va t e Button commitButton ; //Anderungen ubernehmen. . .

3.2 Fenster und GUI-Elemente 81

pub l i c Frames07 ( ){

. . .neuButton . C l i ck += new EventHandler ( buttonEvent ) ;delButton . C l i ck += new EventHandler ( buttonEvent ) ;commitButton . Cl i ck += new EventHandler ( buttonEvent ) ;. . .

Neben den ublichen Initialisierungen wie Position und Große werden die Buttonsim Konstruktor wie gewohnt uber einen EventHandler mit einer Eventfunktionverknupft. Die Initialisierung der ComboBox ist weitgehend identisch zur Initiali-sierung der bereits bekannten GUI-Elemente:

c l a s s Frames07 : Form{

. . .p r i va t e ComboBox myComboBox ;. . .

pub l i c Frames07 ( ){

. . .myComboBox = new ComboBox ( ) ;myComboBox . S i z e = new S i z e (270 , 2 5 ) ;myComboBox . Locat ion = new Point (10 , 1 0 ) ;

myComboBox . SelectedIndexChanged += new EventHandler ( comboBoxEvent ) ;

Contro l s .Add(myComboBox ) ;. . .

Die letzte Codezeile weist der ComboBox einen EventHandler zu, der in diesem Falldie Eventfunktion comboBoxEvent auslost, sobald der Benutzer ein anderes Itemin der ComboBox auswahlt. Diese Anwendung enthalt also zwei unterschiedlicheEventfunktionen, eine fur die Buttons und eine fur die ComboBox. Dies dientlediglich der Ubersichtlichkeit, es war auch ohne Weiteres moglich, alle Events voneiner Eventfunktion verwalten zu lassen. Die ButtonEvent-Funktion enthalt diewichtigsten Zugriffsmethoden der Combobox:

3.2 Fenster und GUI-Elemente 82

pub l i c void buttonEvent ( ob j e c t sender , System . EventArgs e ){

i f ( sender . Equals ( commitButton ) ){i f ( vnTextBox . Text == ”” )vnTextBox . Text = ”unbekannt” ;

i f ( nnTextBox . Text == ”” )nnTextBox . Text = ”unbekannt” ;

i f ( adTextBox . Text == ”” )adTextBox . Text = ”unbekannt” ;

Adresse newAdr = new Adresse ( vnTextBox . Text , nnTextBox . Text , adTextBox . Text ) ;myComboBox . Items .Add(newAdr ) ;}

else //Losch−Button oder Neu−Button{i f ( sender . Equals ( delButton ) ) //Losch−Button{i f (myComboBox . Se l e c ted Index >= 0)

myComboBox . Items . RemoveAt(myComboBox . Se l e c t ed Index ) ;}

myComboBox . Se l e c t ed Index = −1;vnTextBox . Text = ”” ;nnTextBox . Text = ”” ;adTextBox . Text = ”” ;

}}

Wurde der commitButton gedruckt (neue Adresse speichern), wird ein neues Ob-jekt der Klasse Adresse erzeugt und in die ComboBox eingefugt. ComboBoxenenthalten eine Collection mit dem Namen Items, auf die wie gewohnt zugegriffenwerden kann. In diesem Fall verwenden wir die Add-Methode dieser Collection, umdas neue Objekt hinzuzufugen. Wurde der Losch-Button gedruckt, wird zunachstuberpruft, ob ein Element in der ComboBox ausgewahlt ist. Ist kein Element derComboBox ausgewahlt oder ist die ComboBox leer, enthalt die Variable Selec-tedIndex den Wert -1. Falls ein Element ausgewahlt ist, wird es mit der Metho-de RemoveAt() entfernt. Bei Klick auf den Neu- oder den Losch-Button werdenzusatzlich die Textfelder geloscht.

3.2 Fenster und GUI-Elemente 83

Die Eventfunktion der ComboBox sorgt dafur, daß die Textfelder die Daten desaktuell ausgewahlten ComboBox-Elements enthalten:

pub l i c void comboBoxEvent ( ob j e c t sender , System . EventArgs e ){i f (myComboBox . Se l e c t ed Index >= 0){Adresse aktAdr = ( Adresse )myComboBox . Items [myComboBox . Se l e c t ed Index ] ;

vnTextBox . Text = aktAdr . getVorname ( ) ;nnTextBox . Text = aktAdr . getNachname ( ) ;adTextBox . Text = aktAdr . getAdresse ( ) ;

}}

Da die in der ComboBox enthaltenen Objekte immer als Objekt der C#-BasisklasseObject abgelegt werden, mussen sie beim Zugriff entsprechend gecastet werden.

Abbildung 3.9. Anwendung Frames07

3.2 Fenster und GUI-Elemente 84

3.2.8 Beispiel 8 - MDI

MDI (multiple document interface) wird haufig verwendet, wenn eine Anwendungmehrere Fenster enthalt. Die Kindfenster werden dabei nur innerhalb des Haupt-fensters angezeigt. Ein typisches Beispiel fur eine MDI-Anwendung ist die Text-verarbeitung Microsoft Word. In diesem Beispiel verwenden wir der Einfachheithalber das Karteiprogramm aus Beispiel 7. Die Anwendung soll ein Hauptfensterund zwei Kindfenster mit jeweils einer Adresskartei enthalten.

c l a s s Frames08 : Form{Form myChildForm1 ;Form myChildForm2 ;

pub l i c Frames08 ( ){

t h i s . IsMdiContainer = true ;t h i s . S i z e = new S i z e (500 , 500 ) ;t h i s . Text = ”MDI Forms ( mu l t ip l e document i n t e r f a c e ) ” ;

myChildForm1 = new ChildForm ( t h i s ) ;myChildForm2 = new ChildForm ( t h i s ) ;

myChildForm1 . Show ( ) ;myChildForm2 . Show ( ) ;

}. . .

Die Klasse Frames08 dient als Hauptfenster der Anwendung. Die erste Zeile desKonstruktors legt fest, daß es sich dabei um einen MDI-Container handelt, also umeine Form, die weitere Forms enthalten kann. Die Klasse ChildForm reprasentiertein Kindfenster, das innerhalb des Hauptfensters angezeigt wird. Die Besonderhei-ten von Kindfenstern werden spater im Kapitel naher erlautert. Anmerkung: dieshow()-Methode von Forms wird nur dann automatisch beim Programmstart auf-gerufen, wenn die Form mit Application.run(form) gestartet wird. Daher mussendie show-Methoden der Kindfenster explizit aufgerufen werden.

Forms, die als Kindfenster in MDI-Anwendungen verwendet werden sollen,benotigen eine Referenz auf ihr

”Elternfenster“. In dieser Anwendung ubergeben

wir diese Referenz direkt im Konstruktor und setzen die MdiParent-Eigenschaftentsprechend:

pub l i c ChildForm (Form parent ){

t h i s . MdiParent = parent ;. . .

Der ubrige Teil der Childform-Klasse ist identisch zur Adressanwendung aus Bei-spiel 7.

3.2 Fenster und GUI-Elemente 85

Abbildung 3.10. Anwendung Frames08

3.2.9 Beispiel 9 - Radiobuttons und Checkboxen

Das folgende Beispiel zeigt die Verwendung und Gruppierung von Radiobuttonsund Checkboxen. Wahrend Checkboxen im Normalfall die Moglichkeit bieten,mehrere Optionen gleichzeitig auszuwahlen, werden Radiobuttons fur gewohnlichals exklusive Auswahl gruppiert. Der Benutzer kann also nur eine der gruppiertenOptionen auswahlen. Zu Beginn initialisieren wir die benotigten Radiobuttons undCheckboxen. Sie sollen getrennt in unterschiedlichen Groupboxen angeordnet wer-den, um den Unterschied dieser GUI-Elemente zu verdeutlichen. Daher benotigenwir zusatzlich zwei Objekte der C#-Klasse GroupBox.

An dieser Stelle vereinfachen wir die Erzeugung der Objekte im Vergleich zufruheren Beispielen, indem wir bei der Deklarierung der Objektreferenzen direktdie entsprechenden zugehorigen Objekte erzeugen.

3.2 Fenster und GUI-Elemente 86

c l a s s Frames09 : Form{RadioButton opt ion11 = new RadioButton ( ) ;RadioButton opt ion12 = new RadioButton ( ) ;RadioButton opt ion13 = new RadioButton ( ) ;

CheckBox opt ion21 = new CheckBox ( ) ;CheckBox opt ion22 = new CheckBox ( ) ;

GroupBox optionen1Box = new GroupBox ( ) ;GroupBox optionen2Box = new GroupBox ( ) ;. . .

Die Positionierung und Beschriftung der Elemente wird an dieser Stelle nichtnaher beschrieben, da sie sich aquivalent zu den in den vorherigen Kapiteln be-handelten GUI-Elementen verhalt. Dabei ist jedoch zu beachten, daß die Posi-tionierung von GUI-Elementen innerhalb von GroupBoxen immer aus relativenKoodinaten besteht. Der Koordinatenursprung befindet sich an der rechten obe-ren Ecke der GroupBox.

GroupBoxen enthalten eine Collection Controls, genau wie Forms. Mit der Add-Methode konnen GUI-Elemente zu einer GroupBox hinzugefugt werden.

pub l i c Frames09 ( ){

. . .optionen1Box . Contro l s .Add( opt ion11 ) ;optionen1Box . Contro l s .Add( opt ion12 ) ;optionen1Box . Contro l s .Add( opt ion13 ) ;

optionen2Box . Contro l s .Add( opt ion21 ) ;optionen2Box . Contro l s .Add( opt ion22 ) ;. . .

Abschließend reicht es, wenn nur die GroupBoxen zur Controls-Collection der Formhinzugefugt werden:

Contro l s .Add( optionen1Box ) ;Contro l s .Add( optionen2Box ) ;

Standardmaßig sind alle Checkboxen und Radiobuttons deselektiert. Mit derSelect()-Methode konnen sie als Voreinstellung aktiviert werden:

opt ion12 . S e l e c t ( ) ;

3.2 Fenster und GUI-Elemente 87

Abbildung 3.11. Anwendung Frames09

3.2.10 Beispiel 10 - TreeView

Als letztes GUI-Element dieses Kapitels werden wir die Funktionsweise von Tree-Views betrachten. Bei TreeViews handelt es sich um Baumstrukturen wie sie zumBeispiel aus dem Windows Explorer als Verzeichnisbaum bekannt sind. Er bietetdie Moglichkeit, verschiedene Elemente hierarchisch zu ordnen. Das geplante Bei-spielprogramm soll eine kleine Adressverwaltung darstellen, in der zu jeder Adressedie dort wohnenden Mitbewohner eingegeben werden konnen.

Abbildung 3.12. TreeView

Die Anwendung wird bereits einige Beispieleintrage enthalten. Der Benutzer sollzusatzlich die Moglichkeit haben, weitere Adressen und Mitbewohner einzugeben.Auf Losch- und Editierfunktionen wird an dieser Stelle der Ubersichtlichkeit halberverzichtet. Sie konnen nach Fertigstellung des Programms als optionale Ubungimplementiert werden. Der Baum wird in diesem Fall zwei Hierarchieebenen haben:die Adressen stellen die erste Ebene dar, die Mitbewohner die zweite.

Die Initalisierung von TreeViews (Position, Große) verlauft aquivalent wie beianderen in vorherigen Kapiteln besprochenen GUI-Elementen. Knoten innerhalbder TreeView werden durch Objekte der Klasse TreeNode reprasentiert. Um denAufbau einer Knotenhierarchie in TreeViews zu verdeutlichen, erstellen wir einigeBeispielknoten (siehe Abb.).

3.2 Fenster und GUI-Elemente 88

c l a s s Frames10 : Form{TreeView myTree = new TreeView ( ) ;

TreeNode wurzel = new TreeNode ( ) ;TreeNode kindKnoten1 = new TreeNode ( ) ;TreeNode kindKnoten2 = new TreeNode ( ) ;TreeNode kindKnoten3 = new TreeNode ( ) ;. . .

Die Adresse dient hier als Wurzel des Baumteils, die Kindknoten sind die jeweili-gen Mitbewohner. Die Hierarchie wird in folgendem Codeabschnitt festgelegt. Je-der TreeNode enthalt eine Collection Nodes, in die Kindknoten eingefugt werdenkonnen. Da wir in diesem Fall mehrere Kindknoten einfugen wollen, verwenden wirstatt der ublichen Add-Methode die Methode AddRange(). Diese Methode nimmtals Parameter ein Array von TreeNodes an, das wir direkt im Methodenaufruferzeugen. Die anonyme Deklaration des Knotenarrays dient der Ubersichtlichkeitdes Codes, da wir dieses Array nur an dieser Stelle benotigen.

pub l i c Frames10 ( ){wurzel . Text = ”Erlenweg 3” ;kindKnoten1 . Text = ”Max Mustermann” ;kindKnoten2 . Text = ”Gerhard Schr oder ” ;kindKnoten3 . Text = ”Eva Mul ler ” ;

wurze l . Nodes . AddRange(new TreeNode [ ]{ kindKnoten1 , kindKnoten2 , kindKnoten3 } ) ;

Die Anwendung soll dem Benutzer die Moglichkeit geben, weitere Adressenund Mitbewohner eingeben zu konnen. Daher fugen wir zwei Buttons fur dieseFunktionen hinzu. Die Verknupfung von Buttons mit EventHandlern wurde inden vorherigen Kapiteln mehrmals gezeigt und wird daher an dieser Stelle nichterlautert. Dafur betrachten wir die Eventfunktion genauer, die dafur sorgen soll,daß neue Knoten in den Baum eingefugt werden.

3.2 Fenster und GUI-Elemente 89

Die Funktionsweise des Buttons”neue Adresse“ ist recht simpel: da Adressen

die oberste Ebene des Baums darstellen, konnen wir den neuen Knoten direkt zuden Knoten der TreeView hinzufugen:

pub l i c void buttonEvent ( ob j e c t sender , System . EventArgs e ){

. . .i f ( eingabeBox . Text == ”” )eingabeBox . Text = ”unbekannt” ;

TreeNode newNode = new TreeNode ( eingabeBox . Text ) ;

i f ( sender . Equals ( neueAdresse ) )

myTree . Nodes .Add(newNode ) ;. . .

Soll ein neuer Mitbewohner hinzugefugt werden, muss zunachst der zugehorigeAdressknoten ermittelt werden. Uber die Eigenschaft Level kann die Hierarchie-Ebene eines Knotens ermittelt werden. Hat der Benutzer einen Mitbewohnerknotenausgewahlt (Level = 1), wird der zugehorige Adressknoten uber die EigenschaftParent ermittelt. Anschließend wird der neue Knoten mit der Add-Methode hinzu-gefugt und der entsprechende Zweig mit der Methode Expand() ggf. aufgeklappt.

pub l i c void buttonEvent ( ob j e c t sender , System . EventArgs e ){

. . .else i f ( sender . Equals ( neuerMitbewohner ) ){TreeNode aktNode = myTree . SelectedNode ;

i f ( aktNode . Leve l > 0)aktNode = aktNode . Parent ;

aktNode . Nodes .Add(newNode ) ;aktNode . Expand ( ) ;

}}

3.2 Fenster und GUI-Elemente 90

Abbildung 3.13. Anwendung Frames10

4

Grundlagen Patterns

In diesem Kapitel werden die wichtigsten Patterns beschrieben. Ein Pattern ist einEntwurfsmuster fur die Softwareentwicklung. Diese Entwurfsmuster wurden vonder so genannten

”Gang of Four“ [GHJV95] entwickelt. Bei den Entwurfsmustern

handelt es sich um empfohlene Vorgehensweisen bei der Losung von Problemen inder Softwaretechnik. Diese Vorgehensweisen sind gut gepruft und haben sich uberdie Jahre bewahrt.

4.1 Observer Pattern

Das Observer Pattern wird hauptsachlich in der Gestaltung von grafischen Ober-flachen verwendet. Es sorgt dafur, dass bestimmte Bereiche eines Programms, bei-spielsweise Elemente einer grafischen Oberflache, die identische Datenquelle ver-wenden und sich aktualisieren, sobald sich die Daten andern.

Dazu wird ein Programm, welches ein Observer Pattern implementiert, in zweiTeile augeteilt. Einmal die Subject-Klasse, welche die Daten enthalt und zum An-deren die Observer-Klassen und die Observer-Schnittstelle, welche die Daten ein-lesen und anzeigen, ausgeben oder weiter verarbeiten. Die Abbildung 4.1 zeigt dieStruktur dieses Patterns.

Im folgenden Quellcode sieht man ein Beispiel fur ein Programm, welches dasObserver Pattern verwendet. Dort gibt es ein Interface Observer, welches von denbeiden Observer-Klassen RealObserver1 und RealObserver2 implementiert werdenmuss. Das Interface enthalt eine Funktion refresh() mit der alle Observer-Klassenbenachrichtigt werden, wenn sich Daten in der Subject-Klasse geandert haben.Dies geschieht in dem sich alle Observer-Klassen bei der Subject-Klasse in eineArrayList (oder eine beliebige andere Struktur) eintragen. Wenn sich nun Datenandern ruft die Subject-Klasse die Methode notifyObserver() auf, in welcher al-le Observer-Klassen durchlaufen werden und deren refresh() Methode aufgerufenwird. In dieser Methode konnen dann die Observer-Klassen auf die Datenande-rungen reagieren. Das Eintragen und Austragen der Observer-Klassen bei derSubject-Klasse geschieht uber die Methoden addObserver(Observer) und remo-

4.1 Observer Pattern 92

Abbildung 4.1. Observer-Pattern

veObserver(Observer).

import java . u t i l . ArrayList ;

/∗∗ Hier wird das so genannte Observerpa t t ern v o r g e s t e l l t .∗ Dieses d i en t zur Verwaltung von versch iedenen Observern ,∗ welche auf d i e s e l b en Daten zu g r e i f e n .∗/

/∗∗ Dieses I n t e r f a c e s t e l l t den anderen Observern∗ d i e r e f r e s h Methode zur Verfugung . Jeder Observer∗ muss d i e s e Methode implementieren . In der Implementierung∗ der r e f r e s h Methode kann j ed e r Observer mit Daten des∗ Sub j e c t s a r b e i t en . Immer wenn d i e s e s i c h andern s o l l t e∗ r e f r e s h au f ge ru f en werden .∗/

interface Observer{/∗∗ Muss von a l l e n Observern

4.1 Observer Pattern 93

∗ imp lement i er t werden∗/

public void r e f r e s h ( ) ;}

/∗∗ Dies i s t e in Observer .∗ Dieser imp lement i e r t d i e S c h n i t t s t e l l e Observer und∗ g a r an t i e r t dadurch , dass d i e Refreshmethode imp lement i er t i s t .∗ Er war te t auf r e f r e s h s von dem Sub j e c t um zu reag i e r en .∗/

class RealObserver1 implements Observer{//Bezeichnung des Observersprivate int id ;

//Referenz auf das Sub j e c tprivate Subject sub j e c t ;

//Konstruktorpublic RealObserver1 ( int id , Subject sub j e c t ){

this . id = id ;this . s ub j e c t = sub j e c t ;

}

//So r e a g i e r t der Observer b e i einem Not i f ypublic void r e f r e s h ( ){

System . out . p r i n t l n (”RealObserver 1 with id ” + id +” r e f r e s h ed . Current value i s ” +sub j e c t . getValue ( ) ) ;

}}

/∗∗ Dies i s t e ine zwe i t e Obse r ve r k l a s s e .∗ Dieser imp lement i e r t d i e S c h n i t t s t e l l e Observer und∗ g a r an t i e r t dadurch , dass d i e Refreshmethode imp lement i er t i s t .∗ Er war te t auf r e f r e s h s von dem Sub j e c t um zu reag i e r en .∗/

class RealObserver2 implements Observer{//Bezeichnung des Observersprivate int id ;

//Referenz auf das Sub j e c t

4.1 Observer Pattern 94

private Subject sub j e c t ;

//Konstruktorpublic RealObserver2 ( int id , Subject sub j e c t ){

this . id = id ;this . s ub j e c t = sub j e c t ;

}

//So r e a g i e r t der Observer b e i einem Not i f ypublic void r e f r e s h ( ){

System . out . p r i n t l n (”RealObserver 2 with id ” + id +” r e f r e s h ed . Current value i s ” +sub j e c t . getValue ( ) ) ;

}}

/∗∗ In der Su b j e c t k l a s s e werden a l l e Observer in∗ einem Array , e iner ArrayLis t oder e iner ahn l i chen∗ Datens t ruk tur g e s p e i c h e r t .∗ Fa l l s s i c h etwas an den Daten andert ( h i e r : curValue )∗ werden a l l e Observer b e n a c h r i c h t i g t .∗/

class Subject {// L i s t e mit a l l e n Observernprivate ArrayList<Observer> obse rve r ;

//Die zu verwa l tenden Datenprivate int curValue ;

//Konstruktorpublic Subject ( ){

obse rve r = new ArrayList<Observer >() ;curValue = 0 ;

}

//Fugt einen Observer e in und b ena c h r i c h t i g t d i e senpublic void addObserver ( Observer o ){

obse rve r . add ( o ) ;o . r e f r e s h ( ) ;

}

// Ent f e rn t einen Observerpublic void removeObserver ( Observer o ){

4.1 Observer Pattern 95

obse rve r . remove ( o ) ;}

// Benachr i ch t i g t a l l e Observerpublic void not i fyObse rve r ( ){

for ( Observer o : obse rve r ){o . r e f r e s h ( ) ;

}}

// Inkrement i e r t d i e Datenpublic void incValue ( ){

curValue++;not i fyObse rve r ( ) ;

}

//Gibt d i e Daten zur uckpublic int getValue ( ){

return curValue ;}}

/∗∗ Es werden ein Sub j e c t und v i e r Observer e r s t e l l t .∗ Nachdem die Observer b e i dem Sub j e c t angemeldet wurden ,∗ wird der curValue e i n i g e male ink remen t i e r t .∗/

public class ObserverPattern {public stat ic void main ( St r ing [ ] a rgs ){

// E r s t e l l e n des Sub j e c t sSubject sub j e c t = new Subject ( ) ;

// E r s t e l l e n der ObserverRealObserver1 ro1a = new RealObserver1 (1 , sub j e c t ) ;RealObserver1 ro1b = new RealObserver1 (2 , sub j e c t ) ;RealObserver2 ro2a = new RealObserver2 (3 , sub j e c t ) ;RealObserver2 ro2b = new RealObserver2 (4 , sub j e c t ) ;

//Anmelden der Observersub j e c t . addObserver ( ro1a ) ;sub j e c t . addObserver ( ro1b ) ;sub j e c t . addObserver ( ro2a ) ;sub j e c t . addObserver ( ro2b ) ;

//Mehrfaches inkrement ieren der Daten

4.2 MVC Pattern 96

sub j e c t . incValue ( ) ;s ub j e c t . incValue ( ) ;s ub j e c t . incValue ( ) ;

}}

Wie man sieht bietet das Observer Pattern eine praktische Methode mit welcherman dafur sorgen kann, dass bei Datenanderungen in einem Programm andere Pro-grammteile sofort aktualisiert und an die neuen Daten angepasst werden konnen.Wenn man sich die Arbeit vereinfachen mochte, kann man einen in Java vorge-fertigten Observer verwenden. Dazu gibt es die Klass java.util.Observable. Diesereprasentiert das Subject aus dem oben genannten Beispiel. Um einen Observerhinzuzufugen bzw. zu entfernen gibt es die Methoden addObserver bzw. deleteOb-server. Um die Observer zu benachrichtigen benutzt man die Methode notifyOb-servers. Die eigentlichen Observer zeichnen sich dadurch aus, dass sie das Inter-face java.util.Observer implementieren. Dazu ist es notig eine Methode update zuerzeugen. Diese entspricht der refresh Methode aus dem Beispiel. Um nahere In-formationen zu den beiden Klassen zu erhalten, empfieht sich ein Blick in die JavaAPI [Javc].

4.2 MVC Pattern

Abbildung 4.2. Model-View-Control-Pattern

4.2 MVC Pattern 97

Das Kurzel MVC steht fur Model-View-Control. Das MVC-Pattern ist eigent-lich relativ einfach. Es besagt, dass ein Programm in drei unterschiedliche Teileaufgeteilt ist. (siehe Abb.4.2)Der Model-Teil beinhaltet dabei die Daten bzw. den Datenzugriff. Hier konntensich klassische Objekte eines objektorientierten Programms befinden oder auch einDatenbankzugriff oder jede andere Art von Datenverwaltung.Des Weiteren gibt es noch den View-Abschnitt. Hier befinden sich alle Ansichtendes Programms. Diese zeigen normalerweise die Daten des Models an und sindkomplett unabhangig von den Daten implementiert.Der dritte Teil des Patterns ist der Control-Teil. Dieser beinhaltet alle Kon-trollmoglichkeiten, mit welchen ein Benutzer das Programm steuern kann. Auchdieser Teil ist normalerweise komplett unabhangig von den anderen Teilen.

In manchen Programmiersprachen bzw. Anwendungen ist man gezwungen denView-, sowie den Controlteil in einer Struktur unterzubringen. Dieses Pattern wirddann als Document-View Modell bezeichnet.

Die einzelnen Teilbereiche besitzen in einer Umsetzung des MVC-Patterns meis-tens nur Referenzen auf die anderen Teilbereiche. Dabei werden nur die definier-ten Zugriffsmethoden verwendet. Es ist also beispielsweise nicht moglich von einerView-Klasse direkt auf Objekte der Model-Klasse zuzugreifen.

Im folgenden Beispiel wird ein Programm vorgestellt, welches nicht nur einMVC-Entwurfsmuster implementiert, sondern gleichzeitig auch noch ein Observer-Pattern verwendet. Diese Vermischung der beiden Entwurfsmuster kommt sehrhaufig vor, da sie gut zusammen passen. Dabei sind die beiden so verknupft, dasses sich bei den View-Klassen um die Observerklassen des Observer-Patterns han-delt und die Model-Klasse die Subject-Klasse des Patterns reprasentiert.

import java . awt . event . ActionEvent ;import java . awt . event . Act i onL i s t ene r ;import java . u t i l . ArrayList ;

import javax . swing . JButton ;import javax . swing . JFrame ;import javax . swing . JTextFie ld ;

interface View{//Muss von a l l e n Views imp lement i er t werdenpublic void r e f r e s h ( ) ;

}

class RealView extends JFrame implements View{// Id des Fens ters

4.2 MVC Pattern 98

private int id ;

//Referenz auf das Modelprivate Model model ;

// JTextFie ld des Fens tersprivate JTextFie ld txt ;

//Konstruktorpublic RealView ( int id , Model model ){

this . id = id ;this . model = model ;

s e tT i t l e ( ”View ” + id ) ;s e tDe fau l tC loseOperat i on (JFrame .EXIT ON CLOSE) ;

txt = new JTextFie ld ( ”” ) ;getContentPane ( ) . add ( txt ) ;

r e f r e s h ( ) ;

s e tLocat i on (250 , 60 ∗ id ) ;pack ( ) ;s e tV i s i b l e ( true ) ;

}

/∗∗ Wird ausge f u h r t , wenn s i c h etwas an den∗ Daten im Model andert∗/

public void r e f r e s h ( ){txt . setText (

”View ” + id + ” : ” +model . getValue ( ) ) ;

}}

class ButtonView extends JFrame{public ButtonView (Model model ){

// S t e l l t d i e E igenscha f t en des Fens ters e ins e tT i t l e ( ”ButtonView” ) ;s e tDe fau l tC loseOperat i on (JFrame .EXIT ON CLOSE) ;

//Fugt d i e Con t r o l k l a s s e zu dem Button hinzuJButton btn = new JButton ( ” Increment ” ) ;

4.2 MVC Pattern 99

btn . addAct ionLis tener (new Control ( model ) ) ;getContentPane ( ) . add ( btn ) ;

// Ze i g t das Fenster ans e tLocat i on (100 , 100 ) ;pack ( ) ;s e tV i s i b l e ( true ) ;

}}

class Control implements Act ionL i s t ene r {//Referenz auf das Modelprivate Model model ;

//Konstruktorpublic Control (Model model ){

this . model = model ;}

//Wird ausge f u h r t , wenn man den Button a n k l i c k tpublic void act ionPerformed ( ActionEvent evt ){

model . incValue ( ) ;}

}

class Model{// A l l e angemeldeten Viewsprivate ArrayList<View> views ;

//Datenprivate int curValue ;

//Konstruktorpublic Model ( ){

views = new ArrayList<View >() ;curValue = 0 ;

}

//Fugt e ine neue View hinzupublic void addView (View view ){

views . add ( view ) ;view . r e f r e s h ( ) ;

}

// Ent f e rn t e ine View

4.2 MVC Pattern 100

public void removeView (View view ){views . remove ( view ) ;

}

// Benachr i ch t i g t a l l e Viewspublic void not i fyViews ( ){

for (View v : views ){v . r e f r e s h ( ) ;

}}

// Inkrement i e r t d i e Datenpublic void incValue ( ){

curValue++;not i fyViews ( ) ;

}

//Gibt d i e Daten zur uckpublic int getValue ( ){

return curValue ;}

}

public class MVCPattern {public stat ic void main ( St r ing [ ] a rgs ){

/∗∗ E r s t e l l t das Model , in welchem die Daten ,∗ sowie d i e Views v e rwa l t e t werden .∗/

Model model = new Model ( ) ;

//Die Views , welche d i e Daten anze igenRealView view1 = new RealView (1 , model ) ;RealView view2 = new RealView (2 , model ) ;

//Die Views tragen s i c h b e i dem Model e inmodel . addView ( view1 ) ;model . addView ( view2 ) ;

//Die View zur Bedienung des Programmsnew ButtonView ( model ) ;

}}

4.3 Singleton Pattern 101

Der hier gezeigte Quellcode enthalt eine Klasse Model. Diese Klasse reprasen-tiert die Model-Klasse des MVC-Patterns und gleichzeitig die Subject-Klasse desObserver-Patterns. Sie enthalt also alle Daten bzw. Referenzen auf Daten und ver-waltet eine Liste mit allen Observern. Des Weiteren gibt es die Klasse Control,welche naturlich den Control-Bereich des MVC-Patterns darstellt. Uber solch ei-ne Klasse konnen beispielsweise Anderungen an dem Model durchgefuhrt werden.Diese sind jedoch nur uber Zugriffsmethoden moglich, welche die Model-Klasseimplementiert. Dann waren da noch die View-Klassen ButtonView und RealView.Diese implementieren die Schnittstelle View, welche mit der Observer-Schnittstelledes Observer-Patterns gleichzusetzen ist. Da sie diese Schnittstelle implementie-ren, konnen sie sich bei dem Model eintragen und werden uber Datenanderungenbenachrichtigt. Die RealView hat damit dann die Moglichkeit die Daten des Mo-dels aktualisiert darzustellen, sobald sie sich andern. Die ButtonView enthalt nureinen Button, welcher die Control-Klasse verwendet um im Model die Daten zuverandern.

Man sieht also, dass alle Klassen zwar in Beziehungen stehen aber trotz allemunabhangig voneinander implementiert werden. Dadurch hat man die Moglichkeitdas Programm spater durch zusatzliche Controls oder Views zu erweitern, ohnedie Integritat des Programms zu beeinflussen.

4.3 Singleton Pattern

Abbildung 4.3. Singleton-Pattern

Das Entwurfsmuster Singleton wird eingesetzt, wenn von einem Objekt nur ge-nau eine Instanz existieren darf. Man kann zwei Arten der Erzeugung von Single-tons unterscheiden. Die erste Moglichkeit ist ein so genannter Lazy Singleton (Fau-les Einzelstuck). Das bedeutet, dass die Instanz erst dann erzeugt wird, wenn sieauch wirklich benotigt wird.

public f ina l class S ing l e ton {private stat ic S ing l e ton in s tanz ;

private S ing l e ton ( ) {

4.3 Singleton Pattern 102

}

public synchronized stat ic S ing l e ton ge t In s tanz ( ) {i f ( i n s tanz==null )

i n s tanz = new S ing l e ton ( ) ;return i n s t anz ;

}}Die Klasse Singleton ist eine Variante eines Lazy Singleton. Bei der Initialisierungwird lediglich der Speicherbereich fur die Instanz reserviert. Der private Konstruk-tor hat hier keine Funktion. Aber dadurch, dass er als private deklariert ist, wirdverhindert das von Außen weitere Instanzen erzeugt werden konnen. Erst bei demersten Aufruf von

”getInstanz“ wird die Instanz erzeugt und zuruckgegeben. Bei

jedem weiteren Aufruf wird die bereits erzeugte Instanz zuruckgegeben. Bei derVerwendung eines Lazy Singleton muss darauf geachtet werden, dass es bei Ne-benlaufigkeiten durch mehrere Threads nicht zu Problemen kommen kann. Daherist es notig die

”getInstanz“ Methode als

”synchronized“ zu deklarieren. Um die-

ses Problem zu Umgehen kann man einen Eager Singleton (Gieriges Einzelstuck)verwenden.

f ina l class EagerS ing le ton {private f ina l stat ic EagerS ing le ton in s tanz =

new EagerS ing le ton ( ) ;

private EagerS ing le ton ( ) {}

public stat ic EagerS ing le ton ge t In s tanz ( ) {return i n s t anz ;

}}Hier wird die Instanz erzeugt sobald zum ersten mal auf die Klasse referenziertwird. Dieses muss dann bei dem Aufruf der Methode

”getInstanz“ nur noch zuruck-

gegeben werden. Dabei konnen keine Probleme mit der Nebenlaufigkeit mehr auf-treten.

Es kann allerdings im Allgemeinen bei der Verwendung von Singletons zu Pro-blemen kommen. Es ist z.B. nicht immer eindeutig definiert in welchem Bereichdie Instanz einzeln ist. Wenn ein Programm auf mehreren Clustern mit mehrerenThreads lauft, kann es vorkommen dass es mehrere Instanzen gibt. Außerdem kannman Gefahr laufen mit den Singletons eine andere Variante von globalen Variablenzu erzeugen. Ein weiteres Problem besteht darin, wie bereits oben gesehen, dassman auf evtl. vorkommende Nebenlaufigkeiten achten muss. Wegen dieser Pro-bleme sollte man gut uberlegen, wann man ein Singleton einsetzt. Es gibt jedochsinnvolle Einsatzgebiete wie z.B. Pufferspeicher.

4.4 State Pattern 103

4.4 State Pattern

Abbildung 4.4. State-Pattern

Viele Probleme des Alltags lassen sich in Form von endlichen Automaten dar-stellen. Um diese in der Informatik abzubilden wird das State Pattern eingesetzt.Damit ist es moglich die normalen Arbeitsablaufe eines Automatenmodells darzu-stellen, und von Anfang an eine gute Fehlerabsicherung zu gewahrleisten.

In einem Automaten gibt es mehrere verschiedene Zustande, in denen verschie-dene Operationen durchgefuhrt werden konnen. Diese Operationen konnen Zu-standsubergange auslosen.

In diesem Beispiel ist ein Toroffner dargestellt. Hier gibt es die beiden Zustande

”Tor offen“, wenn das Tor geoffnet ist, und

”Tor geschlossen“, wenn das Tor ge-

schlossen ist. Die moglichen Operationen sind”offnen“ und

”schließen“. Die beiden

Operationen konnen einen Zustandsubergang bewirken, wenn ein geschlossenes Torgeoffnet wird oder umgekehrt. In den anderen Fallen soll nichts geschehen, alsowenn ein geschlossenes Tor geschlossen, bzw. ein geoffnetes Tor geoffnet werdensoll. Um dieses Verhalten in einer Programmiersprache umzusetzen wird das StatePattern verwendet.

public class S t a t eB e i s p i e l {

4.4 State Pattern 104

Abbildung 4.5. Automatenmodell eines Toroffners

// Hier s t e h t das Hauptprogramm indem die// Funtk ionsau f ru f e s imu l i e r t werden

public stat ic void main ( St r ing [ ] a rgs ) {Tor meinTor = new Tor ( ) ;meinTor . s c h l i e s s e n ( ) ;meinTor . o f f n en ( ) ;meinTor . o f f n en ( ) ;meinTor . s c h l i e s s e n ( ) ;meinTor . s c h l i e s s e n ( ) ;

}

}

//Die Klasse Tor b i l d e t den Kontext f u r den Automatenclass Tor{

private Zustand z s t ;

public Tor ( ) {z s t=new TorGeschlossen ( this ) ;

}

public void setzeZustand ( Zustand z s t ) {this . z s t=z s t ;

}

public void o f f n en ( ) {z s t . o f f n en ( ) ;

}

public void s c h l i e s s e n ( ) {z s t . s c h l i e s s e n ( ) ;

}

4.4 State Pattern 105

}

//Die Abs t rak te Klasse Zustand b i l d e t d i e Grundlage// f u r konkre te Zustandeabstract class Zustand{

protected Tor meinTor ;

public Zustand (Tor meinTor ) {this . meinTor=meinTor ;

}

public void o f f n en ( ) {System . out . p r i n t l n

( ”Fur d i e s en Zustand n i cht d e f i n i e r t ! ” ) ;}

public void s c h l i e s s e n ( ) {System . out . p r i n t l n

( ”Fur d i e s en Zustand n i cht d e f i n i e r t ! ” ) ;}

}

//TorGeschlossen i s t e ine Implementierung// der ab s t r a k t en Klasse Zustandclass TorGeschlossen extends Zustand{

public TorGeschlossen (Tor meinTor ) {super (meinTor ) ;

}

public void o f f n en ( ) {System . out . p r i n t l n ( ”Das Tor wird g e o f f n e t . ” ) ;meinTor . setzeZustand (new TorOffen (meinTor ) ) ;System . out . p r i n t l n ( ”Das Tor i s t o f f e n . ” ) ;

}}

//TorOffen i s t e ine zwe i t e Implementierung// der ab s t r a k t en Klasse Zustandclass TorOffen extends Zustand{

public TorOffen (Tor meinTor ) {super (meinTor ) ;

}

4.4 State Pattern 106

public void s c h l i e s s e n ( ) {System . out . p r i n t l n ( ”Das Tor wird g e s ch l o s s en . ” ) ;meinTor . setzeZustand (new TorGeschlossen (meinTor ) ) ;System . out . p r i n t l n ( ”Das Tor i s t g e ch l o s s en . ” ) ;

}}

Das Beispiel zeigt eine Implementierung des Tor Automaten in der Programmier-sprache Java. Im Hauptprogramm wird ein neues Tor angelegt, und es werdenverschiedene Operationen darauf ausgefuhrt. Die eigentliche Logik des State Pat-terns verbirgt sich in den weiteren Klassen.

Die Klasse”Tor“ bildet den Kontext fur den Automaten. Das bedeutet, dass

der Ablauf in einem Objekt dieser Klasse stattfindet. Dazu muss das Objekt wis-sen in welchem Zustand es sich gerade befindet. Dieser wird in der Variable

”zst“

gespeichert. Des Weiteren mussen alle moglichen Funktionen an den Zustand wei-tergereicht werden. Zusatzlich muss hier auch eine Funktion existieren, mit der esmoglich ist den Zustand zu wechseln.

Die abstrakte Klasse”Zustand“ bildet die Grundlage fur die weiteren Zustande.

Im Konstruktor muss angegeben werden fur welches Tor dieser Zustand vorliegt.Dadurch ist es moglich, mehrere Tore parallel zu betreiben. Des Weiteren werdenhier alle Funktionen erstellt, die in dem Automaten vorkommen konnen. Die hiererstellten Funktionen konnen von den konkreten Klassen uberschrieben werden.Wenn das nicht getan wird werden die hier implementierten Funktionen aufge-rufen. Da in der Klasse

”Tor“ Objekte der abstrakten Klasse Zustand verwendet

werden, ist sichergestellt das nur konkrete Implementierungen der Klasse Zustandvorkommen konnen.

Hier gibt es zwei konkrete Implementierungen der Klasse Zustand. Die ers-te beschreibt den Zustand, wenn das Tor geschlossen ist. Da in diesem Zustandnur die Operation

”offnen“ von Belang ist, wird auch nur diese implementiert.

Entgegengesetzt dazu wird im anderen Zustand, wenn das Tor offen ist, nur dieOperation

”schliessen“ implementiert. Dadurch ist sichergestellt, dass immer nur

passende Operationen durchgefuhrt werden. Falls sich das Tor im Zustand”Tor

geschlossen“ befindet, und die Operation “schliessen“ aufgerufen wird, wird auf dieImplementierung in der abstrakten Klasse zuruckgegriffen, die sich um die Fehler-ausgabe kummert.

Die Ausgabe des Programmes lautet:

Kein Zustand d e f i n i e r t !Das Tor wird g e o f f n e t .Das Tor i s t o f f e n .! ∗ ! ∗ ! Das Tor i s t b e r e i t s g e o f f n e t ! ∗ ! ∗ !Das Tor wird g e s ch l o s s en .

4.5 Factory Pattern 107

Das Tor i s t g e ch l o s s en .Kein Zustand d e f i n i e r t !

Damit sind alle Falle abgedeckt, und es kann kein nicht definierter Zustanderreicht werden.

4.5 Factory Pattern

Das so genannte Factory Pattern, bietet dem Programmierer die Moglichkeit aneinem zentralen Punkt, eines Programms, Objekte zu erstellen und zu verwalten.Es ist oftmals notig eine Ubersicht uber die Objekte, welche in dem Programmverwendet werden, zu besitzen und die Moglichkeit zu haben von einer zentralenStelle aus auf diese Objekte zugreifen zu konnen.

Um dies zu realisieren, erstellt man eine Methode, an einem zentralen Punkt,beispielsweise einer Factory Pattern Klasse, welche bei der Ubergabe einer be-stimmten ID ein Objekt einer bestimmten Klasse zuruck gibt. Dieses Objekt, wel-ches in der Methode erstellt wurde kann dann leicht in einem Array oder einerahnlichen Datenstruktur abgespeichert werden, so dass man immer einen Zugriffauf die erstellten Objekte hat. Das Ganze ist wohl sicher einfacher zu verstehen,wenn man ein Beispiel dazu sieht, also kommt hier der dazu passende Quellcode(dieser ist auch nochmal in der Datei FactoryPattern.java zu finden):

interface Vehic l e {public int getCountOfTires ( ) ;public St r ing getName ( ) ;

}

class Car implements Vehic l e {public int getCountOfTires ( ){

return 4 ;}public St r ing getName (){

return ”Car” ;}

}

class Motorcycle implements Vehic l e {public int getCountOfTires ( ){

return 2 ;}public St r ing getName (){

return ”Motorcycle ” ;}

4.5 Factory Pattern 108

}

class Factory{public stat ic f ina l int CAR = 0 ;public stat ic f ina l int MOTORCYCLE = 1 ;

public Vehic l e c r e a t eVeh i c l e ( int i ){switch ( i ){case CAR:

return new Car ( ) ;case MOTORCYCLE:

return new Motorcycle ( ) ;default :

throw new I l l ega lArgumentExcept ion (”Wrong v eh i c l e number ! ” ) ;

}}

}

public class FactoryPattern {public stat ic void main ( St r ing [ ] a rgs ){

Factory f = new Factory ( ) ;

Veh ic l e newCar =f . c r e a t eVeh i c l e ( Factory .CAR) ;

Veh ic l e newMotorcycle =f . c r e a t eVeh i c l e ( Factory .MOTORCYCLE) ;

System . out . p r i n t l n (newCar . getName ( ) + ” : ” +newCar . getCountOfTires ( ) +” t i r e s ” ) ;

System . out . p r i n t l n ( newMotorcycle . getName ( )+ ” : ” + newMotorcycle . getCountOfTires ( )+ ” t i r e s ” ) ;

}}

Fur dieses Beispiel wurde ein Interface Vehicle geschrieben, welches zwei Me-thoden getCountOfTires() und getName() zur Verfugung stellt. Statt eines Inter-faces konnte man auch eine vererbende Klasse schreiben. Das Interface bzw. dieOberklasse wurde dann von verschiedenen Unterklassen implementiert bzw. geerbtwerden. In unserem Beispiel sind das die Klassen Car und Motorcycle. Diese imple-mentieren die beiden Methoden von Vehicle und sind in diesem Beispiel die Klas-

4.5 Factory Pattern 109

sen, von welchen wir Objekte erstellen mochten. Zusatzlich zu diesen Datenstruk-turen wird noch die Klasse Factory implementiert, in welcher die Datenstrukturenverwaltet werden konnen. Diese Klasse enthalt nur ein paar Konstanten, sowiedie Methode createVehicle(int). Mit dieser Methode konnen auf einfachstem Wegeinzelne Instanzen von Car und Motorcycle erstellt werden. Dazu ubergibt maneinfach die passende ID, bzw. die Konstante, welche die ID als Wert besitzt. Dieswird dann auch in der Main-Methode der Klasse FactoryPattern durchgefuhrt.Hier wird dann einfach nur noch eine Instanz der Factoryklasse erstellt und dieMethode createVehicle passend aufgerufen.

Es ist also ganz einfach die Erstellung von Objekten in eine separate Klasseauszulagern. Man erhalt dadurch die Moglichkeit, an einer zentralen Stelle Objek-te zu erstellen und diese zu verwalten.

Weitere interessante Informationen zu den hier vorgestellten Patterns, so wieweiteren anderen, welche interessant fur die grafische Programmierung sind, findetman in dem Buch

”Spieleprogrammierung Gems 1“ [DeL02]:

5

Grundlagen Java 2D

5.1 Zeichnen

Im folgenden Abschnitt wird beschrieben, wie man in Java zeichnen kann. In Javagibt es im Grunde zwei verschiedene Strukturen, welche man benutzen kann umGrafiken zu zeichnen. Es gibt die Struktur, welche vor der Einfuhrung von Java2Dverwendet wurde und Java2D, welches zu dem jetzigen Zeitpunkt benutzt werdensollte. Damit die Unterschiede der beiden Strukturen klar werden, werden dieseim folgenden Abschnitt an Quellcodes dargestellt und naher erlautert.Zu dem Thema Java2D gibt es ein Tutorial [SUNa] in dem weitere Hilfesellunggegeben wird.

5.1.1 Zeichnen vor Java2D

Abbildung 5.1. Ausgabe des Programms Draw01

Auch ohne Java2D ist es relativ einfach Grafiken zu zeichnen. Der folgendeQuellcode zeichnet dazu auf ein JPanel und verwendet Methoden, welche zum di-rekten Zeichnen verwendet werden. Beispielsweise gibt es Methoden um eine Linievon einem Punkt zu einem anderen Punkt zu zeichnen oder eine Methode, welche

5.1 Zeichnen 111

ein Rechteck mit bestimmten Kantenlangen und einem Startpunkt zeichnet. Die-se Art des Zeichnens wird in dem folgenden Quellcode naher dargestellt, welchernochmal in der Datei Draw01.java zu finden ist. (siehe Abb. 5.1)

import java . awt . Color ;import java . awt . Graphics ;

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

/∗∗ Die Klasse Draw01Panel z e i g t wie man ohne∗ Java2D auf e in JPanel z e i c hne t .∗/

public class Draw01{public stat ic void main ( St r ing [ ] a rgs ){

JFrame frame = new JFrame ( ) ;frame . s e tT i t l e ( ”Draw01” ) ;frame . s e tDe fau l tC loseOperat ion (

JFrame .EXIT ON CLOSE) ;

Draw01Panel drawPanel = new Draw01Panel ( ) ;frame . getContentPane ( ) . add ( drawPanel ) ;

frame . s e t S i z e ( 500 , 230 ) ;frame . s e tV i s i b l e ( true ) ;

}}

class Draw01Panel extends JPanel{public Draw01Panel ( ){

r epa in t ( ) ;}public void paintComponent ( Graphics g ){

// Uberschre iben des a l t e n B i l d e ssuper . paintComponent ( g ) ;

//Viereck ze ichneng . drawRect (20 , 20 , 50 , 5 0 ) ;

// Au s g e f u l l t e s Viereck ze ichneng . s e tCo lo r ( Color .RED) ;g . f i l l R e c t (20 , 80 , 50 , 8 0 ) ;

5.1 Zeichnen 112

//Zeichnen e ine s Polygonsint [ ] xCoords = {120 ,145 ,170} ;int [ ] yCoords = {70 ,20 ,70} ;g . f i l l P o l y g o n (

xCoords ,yCoords ,xCoords . l ength ) ;

//Rand des Polygonsg . s e tCo lo r ( Color .BLACK) ;g . drawPolygon (

xCoords ,yCoords ,xCoords . l ength ) ;

//Kreis− bzw . Kreisbogen ze ichneng . s e tCo lo r (

new Color (255 , 255 , 0 ) ) ;g . f i l l A r c (

220 , 20 , 100 , 100 , 45 , 200 ) ;

//Rand des Kre ises ze ichneng . s e tCo lo r (

Color .BLACK) ;g . drawArc (

220 , 20 , 100 , 100 , 0 , 360 ) ;

//Oval ze ichneng . drawOval (

370 , 20 , 100 , 150 ) ;}}

In der Main-Methode des Programms wird ein normales JFrame erstellt, wiees in Abschnitt 2.1 gezeigt wird. Des Weiteren wird in dieses JFrame ein selbstdefiniertes Draw01Panel, welches von JPanel erbt, eingefugt. Dieses Panel besitztdie Methode repaintComponent(Graphics). Die Implementierung dieser Metho-de uberschreibt die gleichnamige Funktion der Oberklasse JPanel und ist fur dasZeichnen aller Elemente dieses JPanels zustandig. Immer wenn sich etwas an derGrafik andern soll oder die Grafiken neu gezeichnet werden mussen wird dieseFunktion aufgerufen. Dies geschieht beispielsweise, wenn man die Fenstergroßeverandert oder ahnliche Aktionen durchfuhrt. Zusatzlich kann der Programmie-rer dieses neu zeichnen selbst steuern, indem er die Methode repaint() des Panelsaufruft. Dadurch wird die repaintComponent(Graphics)-Methode ebenfalls aufge-

5.1 Zeichnen 113

rufen und das Fenster wird neu gezeichnet. Dies wird dann auch einmal in demKonstruktor des Panels durchgefuhrt.

Wenn man sich nun die paintComponent(Graphics)-Methode anschaut erkenntman das hier das Zeichnen stattfindet. Dazu wird normalerweise immer dasGraphics-Objekt, welches von dem System an die Methode ubergeben wird, ver-wendet. Dieses Objekt ist fur das Zeichnen von Objekten zustandig.

Als erster Aufruf in der paintComponent(Graphics)-Methode wird der Aufrufsuper.paintComponent(g) durchgefuhrt. Dieser fuhrt dazu, dass die paintCompo-nent(Graphics) Methode der Oberklasse ausgefuhrt wird. Durch diese wird dasBild vor dem neu zeichnen geloscht. Dies ist sehr wichtig, da sonst unschone Ef-fekte auftreten, bei welchen mehrere Bilder ubereinander gezeichnet werden. Da-nach werden die eigentlichen Grafiken gezeichnet. Dazu bietet das Graphics-Objektmehrere Methoden, mit welchen man Linien, Rechtecke, Kreise o.a. zeichnen kann.Die drawRect(int,int,int,int) Methode zeichnet beispielsweise ein Rechteck. Somitzeichnet der Aufruf der Methode g.drawRect(20, 20, 50, 50) ein Rechteck mitder Breite und Hohe von 50 Pixeln an die Koordinate (20,20). Dabei wird nurder Rand des Rechtecks gezeichnet. Um ein Rechteck komplett auszufullen mussman die Methode g.fillRect(int, int, int, int) verwenden. Des Weiteren sieht manleicht wie man Polygone (Objekte mit mehreren Ecken) zeichnen kann, indem manzwei Arrays mit x- und y-Koordinaten an die Methode g.drawPolyon(int[],int[],int)ubergibt. Aber auch Kreise oder Ovale sind mit einem Graphics-Objekt zeichen-bar. So bietet einem die Methode g.drawOval(int,int,int,int) die Moglichkeit einOval zu zeichnen und die Methode g.drawArc(int,int,int,int,int,int) ermoglicht esHalbkreise oder Kuchendiagramme zu zeichnen, wobei die beiden letzten Parame-ter angeben, in welchem Winkel die Kreislinie anfangt und uber wieviele Grad siesich erstreckt.

5.1.2 Zeichnen mit Java2D

Mit der Einfuhrung von Java2D haben sich einige Dinge geandert, wenn es darumgeht eigene Grafiken zu erstellen. Man kann zwar auch heute noch die alten Me-thoden und Klassen verwenden, jedoch bietet Java2D einige neue Moglichkeitenund einfachere Strukturen um Grafiken zu zeichnen.

Hierbei verwendet Java2D ein etwas indirekteres System zum Zeichnen vonObjekten. Anstatt direkt Methoden wie drawRect() o.a. zu verwenden, benutztdas System so genannte Shapes. Shapes sind grafische Objekte, welche vor demZeichnen definiert werden. Diese konnen dann ganz einfach mit einem Aufruf nachder Definition gezeichnet werden. Des Weiteren hat man die Moglichkeit Objektevoneinander abzuziehen oder sie zusammen zu addieren und somit neue Objektezu erstellen. Dies wird durch die so genannten Areas ermoglicht.

5.1 Zeichnen 114

In dem hierauf folgenden Quellcode sieht man wie man einfache Objekte zeich-nen kann und wie man Areas benutzen kann um Shapes miteinander zu kombi-nieren. Auch in diesem Programm gibt es, wie im vorherigen Abschnitt 5.1.1 eineMain-Methode, welche wieder ein Panel in ein JFrame einfugt, welches wir selbstimplementiert haben und welches fur die Zeichnung der Grafiken verantwortlichist. Da sich hier nur die paintComponent(Graphics)-Methode verandert hat wirdin dem folgenden Quellcode nur die Implementierung dieser Methode dargestellt.Der kompletten Quellcode ist nochmal in der Datei Draw02.java enthalten. (sieheAbb. 5.2)

Abbildung 5.2. Ausgabe des Programms Draw02

5.1 Zeichnen 115

public void paintComponent ( Graphics g ){// Uberschre iben des a l t e n B i l d e ssuper . paintComponent ( g ) ;

/∗∗ Das ubergebene Graphics−Objekt i s t∗ genau genommen ein Graphics2D Objekt∗ und muss de sha l b nur g e c a s t e t werden .∗/

Graphics2D g2d = ( Graphics2D ) g ;

// E r s t e l l e n e ine s Rec tang l e sRectangle2D . Double r e c t =

new Rectangle2D . Double (20 , 20 , 100 ,100) ;

// E r s t e l l e n e ine s RoundRectanglesRoundRectangle2D . Double roundRect =

new RoundRectangle2D . Double (150 ,20 ,100 ,100 ,40 ,40 ) ;

// E r s t e l l e n e iner CubicCurveint s t a r t x = 300 ;int s t a r t y = 120 ;int p1x = 320 ;int p1y = 20 ;int p2x = 350 ;int p2y = 40 ;int endx = 400 ;int endy = 120 ;

g2d . s e tCo lo r ( Color .BLACK) ;CubicCurve2D . Double cubicCurve =

new CubicCurve2D . Double (s ta r tx , s ta r ty , p1x , p1y , p2x , p2y , endx , endy ) ;

/∗∗ Er s t e l l e n von Rectang l e s zum Zeichnen∗ der Knotenpunkte der CubicCurve∗/

Rectangle2D . Double rectCubicP1 =new Rectangle2D . Double ( p1x−2,p1y−2 ,4 ,4) ;

Rectangle2D . Double rectCubicP2 =new Rectangle2D . Double ( p2x−2,p2y−2 ,4 ,4) ;

5.1 Zeichnen 116

Rectangle2D . Double r e c tCub i cSta r t =new Rectangle2D . Double ( s ta r tx −2, s ta r ty −2 ,4 ,4) ;

Rectangle2D . Double rectCubicEnd =new Rectangle2D . Double ( endx−2,endy−2 ,4 ,4) ;

//Nutzen e iner Area

/∗∗ Er s t e l l e n e iner Area bes tehend∗ aus einem Rectang le∗/

Rectangle2D . Double areaRect =new Rectangle2D . Double (450 , 20 , 100 , 100 ) ;

Area area1 = new Area ( areaRect ) ;

// E r s t e l l e n e iner Area bes tehend aus einem ArcArc2D . Double areaArc =

new Arc2D . Double (460 ,30 ,80 ,80 ,0 ,360 ,Arc2D .CHORD) ;

Area area2 = new Area ( areaArc ) ;

//Abziehen der areaArc von der areaRectarea1 . subt rac t ( area2 ) ;

//Zeichnen a l l e r Shapesg2d . s e tCo lo r ( Color .BLACK) ;g2d . draw ( r e c t ) ;g2d . f i l l ( roundRect ) ;g2d . draw ( cubicCurve ) ;g2d . f i l l ( rectCubicP1 ) ;g2d . f i l l ( rectCubicP2 ) ;g2d . f i l l ( r e c tCub i cSta r t ) ;g2d . f i l l ( rectCubicEnd ) ;g2d . f i l l ( area1 ) ;

}

Als allererste Methode wird auch hier zuerst der Bildschirm geloscht, so dass al-te Grafiken nicht einfach uberzeichnet werden. Danach wird das Graphics-Objekteinfach in ein Graphics2D-Objekt gecastet. Dies ist ohne weiteres moglich, da essich bei dem Objekt g bereits um ein Graphics2D-Objekt handelt. Danach wirddieses Objekt an die Variable g2d ubergeben, mit welcher wir weiter arbeiten wer-den.

Um nun Grafiken zu zeichnen, mussen zuerst, wie oben beschrieben, Shapeserstellt werden. Ein Shape in Java ist dabei eigentlich nur ein Interface, welches

5.2 Translation, Rotation und Skalierung 117

jedoch von verschiedenen Klassen wie z.B. Rectangle2D implementiert wird, wo-durch diese Klasse ebenfalls zu einem Shape wird. Nun werden wie gewohnt Objek-tinstanzen von diesen Klassen erstellt. Dabei stellt beispielsweise ein Objekt vomTyp Rectangle2D.Double ein Rechteck dar. RoundRectangle.Double stellt hinge-gen ein Rechteck mit abgerundeten Ecken dar. Dabei ist zu beachten, dass z.B.auch die Klasse Rectangle2D.Float existiert. Der einzige Unterschied zwischen die-ser und Rectangle2D.Double ist die Genauigkeit mit welcher das Objekt berechnetwird, wobei die Objekte mit der Float-Endung etwas schneller berechnet werdenkonnen aber auch etwas ungenauer abgespeichert werden.

Außer den beiden Rechtecktypen wird in dem Quellcode noch eine CubicCurveerstellt, welche eine Kurve dargestellt, welche uber ihre Start- und Endpunkte,sowie uber zwei Kontrollpunkte berechnet wird. Diese Kontrollpunkte, zusammenmit dem Start- und dem Endpunkt, bestimmten dabei die Steigung und Richtungder Kurve. Das Prinzip wird wohl am klarsten, wenn man einfach mal das ganzemit verschiedenen Koordinaten ausprobiert. In dem Beispiel werden die Koordi-naten der Punkte zusatzlich noch als kleine Vierecke dargestellt, um sie bessererkennen zu konnen.

Zu guter letzt wird noch eine Area erstellt. Mit Areas bekommt man dieMoglichkeit geboten verschiedene Objekte zu verknupfen und sogar mathemati-sche Funktionen darauf auszufuhren. Dazu ubergibt man an ein Area-Objekt einShape, beispielsweise ein Rechteck. Erstellt man nun noch eine zweite Area miteinem zweiten Rechteck kann man diese miteinander kombinieren und Funktionenwie z.B. subtract(Area) oder intersect(Area) darauf verwenden. Diese Methodenerrinnern an die Mengenfunktionen der Mathematik, mit welchen man Menge zu-sammenfuhren oder abziehen kann.

Somit hat man die Moglichkeit mit relativ einfachen Mitteln komplexere Grafi-ken zu erstellen ohne jeden einzelnen Strich direkt zu erstellen. Man hat viel mehrdie Moglichkeit Objekte zu definieren und sie erst danach zu zeichnen.

5.2 Translation, Rotation und Skalierung

Um mit Java2D geometrische Veranderungen in den zu zeichnenden Grafiken zuverwirklichen, werden affine Transformationen eingesetzt. Das bedeutet das Liniendie vor der Transformation parallel verlaufen sind, diese Eigenschaft auch hinter-her noch besitzen. Bei einer affinen Transformation handelt es sich im Grunde umeine einfache Matrixmultiplikation. Dazu werden alle Koordinaten mit der Trans-formationsmatrix multipliziert. Bei der Transformationsmatrix handelt es sich umeine 3x3 Matrix mit folgendem Aufbau.

5.2 Translation, Rotation und Skalierung 118a c txb d ty0 0 1

Uber die Variablen a, b, c und d konnen Drehung, Spiegelung, Ska-

lierung und Scherung realisiert werden. die Variablen tx und ty dienen dazu dieGrafik zu verschieben.In Java2D konnen Transformationen direkt auf einem Graphics2D Objekt oderauf einer Area angewendet werden. Wenn eine Transformation direkt auf ein Gra-phics2D Objekt angewendet wird, wird damit der Ursprung des Koordinatensys-tems verandert. Der Standardeinstellung ist, dass der Nullpunkt in der oberenlinken Ecke des Fensters liegt. Die positiven Koordinaten befinden sich unter bzw.rechts vom Nullpunkt. Um ein normales kartesisches Koordinatensystem zu erzeu-gen muss einmal an der x-Achse gespiegelt werden, außerdem muss in den meistenFallen der Nullpunkt verschoben werden. Um dies zu erreichen gibt man eine

Transformationsmatrix an, die beide Aufgaben gleichzeitig erledigt.

1 0 dx

0 −1 dy

0 0 1

Dabei stehen dx und dy fur die Verschiebung. Die negative 1 an Stelle

”d“ sorgt

dafur, dass das Koordinatensystem an der x-Achse gespiegelt wird.Um nun die zu zeichnenden Grafiken zu Transformieren kann entweder das ge-samte Graphics2D Objekt transformiert werden, was nicht ratsam ist da dann jaalle Grafiken gleichermaßen beeinflusst werden. Der bessere Weg ist, die Grafikenin eine Area zu packen. Diese Area kann dann transformiert werden. Dabei wirktsich die Transformation auf alle darin befindlichen Objekte aus.Um die eigentliche Transformation zu beschreiben gibt es mehrere Methoden.Der komplexeste aber auch vielseitigste Weg ist die Eingabe der Transformations-matrix. Dazu werden dem

”AffineTransform“ Objekt mit der Methode

”setTrans-

form“ 6 Werte ubergeben. Diese stehen fur die ersten beiden Zeilen der Matrix.Damit lassen sich alle moglichen Transformationen erzeugen.Neben dieser Variante gibt es auch noch fur jede einzelne Transformationsart ei-gene Methoden.

•”scale“ dient dazu die Grafik zu skalieren. Hier werden der Methode zwei

”dou-

ble“ Werte ubergeben. Jeweils einen fur die Skalierung in x bzw. y Richtung.•

”shear“ ermoglicht es das Objekt zu scheren. Das bedeutet eine Verzerrung

entlang der Achse zu veranlassen. Auch hier werden wieder zwei”double“ Werte

ubergeben. Je einer fur die Zerrung in x bzw. y Richtung.•

”translate“ sorgt fur eine Verschiebung. Auch hier gibt es wieder zwei

”double“

Werte fur die Verschiebung in x und y Richtung.• “rotate“ rotiert das Objekt um den Nullpunkt des Graphics2D Objektes. Es

gibt eine weitere Rotate Methode mit 3 Werten. Hierbei gibt der erste denWinkel der Rotation an, und die beiden anderen konnen den Rotationspunktverschieben.

Bei allen Moglichkeiten der Translation dient die Transformation bzw. das Koor-dinatensystem des Graphics2D Objektes als Grundlage.Um die Transformation des Graphics2D Objekts zu verandern konnen zwei Metho-

5.2 Translation, Rotation und Skalierung 119

den eingesetzt werden. Die erste Moglichkeit ist”setTransform“ hiermit wird die

bisherige Transformation uberschrieben. Die zweite Moglichkeit ist”transform“.

Hierbei werden die verschiedenen Transformationen konkateniert. Dabei wird diezuletzt angewandte Transformation zuerst verarbeitet.Im Nachfolgenden Beispiel wird zunachst das Koordinatensystem des Panels inein kartesisches Koordinatensystem uberfuhrt. Anschließend wird ein Rechteckgezeichnet auf dem alle moglichen Arten der Transformation angewendet werden.Um die Transformationen zu realisieren wird bei der Verschiebung des Koordi-natensystems direkt eine Transformationsmatrix angegeben. Um das Rechteck zutransformieren werden die einzelnen Funktionen verwendet.

/∗∗∗ Aufruf der paintComponent Methode , um s i c h e r z u s t e l l e n ,∗ dass auf einem sauberen Arb e i t s b e r e i c h g e z e i c hne t wird .∗/

super . paintComponent ( g ) ;/∗∗∗ Casten des Graphics Objekt auf e in Graphics2 Objekt∗/

Graphics2D g2d = ( Graphics2D ) g ;

/∗∗∗ Erzeugen von versch iedenen g ra f i s c h en Objekten∗/

Rectangle2D . Double r e c t = new Rectangle2D . Double ( 0 , 0 , 2 0 , 2 0 ) ;Line2D . Double xax i s = new Line2D . Double ( −200 ,0 ,200 ,0) ;Line2D . Double yax i s = new Line2D . Double (0 ,200 ,0 , −200) ;

/∗∗∗ Erzeugen e iner Transformation , d i e das Koordinatensystem∗ in das Zentrum des Panels v e r s c h i e b t .∗ Zu s a t z l i c h wird das Koordinatensystem an der x Achse g e s p i e g e l t .∗/

Aff ineTransform afg2d = new Aff ineTransform ( ) ;afg2d . setTransform (1 ,0 ,0 ,−1 , this . getWidth ( )/2 , this . getHeight ( ) / 2 ) ;

/∗∗∗ Eine zwe i t e Transformation d i e a l l e Arten∗ der Transformation auf e in Rechteck anwendet .∗/

Aff ineTransform a f = new Aff ineTransform ( ) ;a f . t r a n s l a t e ( 5 0 , 7 0 ) ;a f . r o t a t e (Math . toRadians ( 2 0 ) ) ;a f . s c a l e ( 2 , 2 ) ;a f . shear ( 1 , 2 ) ;

5.2 Translation, Rotation und Skalierung 120

/∗∗∗ Anwenden der Transformation auf das Graphis2D Objekt∗/

g2d . setTransform ( afg2d ) ;/∗∗∗ Zeichnen der Koordinatenachsen∗/

g2d . draw ( xax i s ) ;g2d . draw ( yax i s ) ;/∗∗∗ Verpacken des Rechtecks in e ine Area , um∗ d i e Transformation ausf uhren zu konnen .∗/

Area ar = new Area ( r e c t ) ;ar . t rans form ( a f ) ;/∗∗∗ Zeichnen der Area∗/

g2d . draw ( ar ) ;

Das Ergebnis der Transition sieht dann wie folgt aus: Man kann erkennen, dass das

Abbildung 5.3. Transition des Rechtecks

Koordinatensystem in die Mitte des Fensters verschoben wurde. Die verschiedenenTransitionen werden nur auf das Rechteck angewendet. Dieses ist geschert, skaliert,gedreht und verschoben.

5.3 Fonts 121

5.3 Fonts

In diesem Abschnitt wird erlautert, wie man mit der Hilfe von Java2D sehr einfacheigene Texte in eine grafische Darstellung einfugt. Dies funktioniert genauso ein-fach wie das Zeichnen eines Rechtecks oder eines anderen beliebigen Objektes (sie-he Abschnitt 5.1.2). Genau wie beispielsweise die Funktion Graphics.drawRect()gibt es auch eine Funktion Graphics.drawString(String, int, int), mit welcher maneinen Schriftzug zeichnen kann.

Als Argumente bekommt die Funktion zuerst den anzuzeigenden Text uberge-ben, als zweites die x-Koordinate und als drittes die y-Koordinate. Fugen wir nuneinen solchen Funktionsaufruf in unsere paintComponent(Graphics)-Methode einwird ein einfacher Text angezeigt. Dieser ist zwar nicht schon aber er wird wenigs-tens schon mal angezeigt. Zur Anzeige verwendet Java2D eine Standardschrift.Um den Text nun etwas schoner aussehen zu lassen konnen wir die Anzeigeschriftfur diesen Text andern, indem wir die derzeitige Schrift, welche das Graphics-Objekt verwendet andern. Dazu muss man dem Graphics-Objekt einfach uber diesetFont(Font)-Methode ein Objekt vom Typ Font ubergeben, welches wir vorherdefinieren.

Der Konstruktor eines Font-Objekts bekommt normalerweise drei Parameterubergeben. Der erste Parameter gibt den Namen der Schriftart an, der zweiteden Stil als Integer und der dritte Wert stellt die Schriftgroße der Schriftart dar.Die einzelnen moglichen Werte, die als Stile verwendet werden konnen, werdenals Konstanten in der Klasse Font zur Verfugung gestellt. Dazu gehoren die WertePLAIN, BOLT und ITALIC, was soviel wie standard, fett und kursiv bedeutet. Umherauszufinden, welche Schriftarten in dem derzeitigen System existieren, kann diestatische Methode GraphicsEnvironment.getLocalGraphicsEnvironment() verwen-det werden um das derzeitige lokale GraphicsEnvironment zu erhalten. Dieses stelltwiederum die Methode getAvailableFontFamilyNames() zur Verfugung, welche einString-Array mit allen derzeitig verfugbaren Schriften zuruckgibt. Hier kann mansich eine beliebige Schrift heraus suchen oder uberprufen ob eine bestimmte Schriftauf einem System verfugbar ist.

Der folgende Quellcode, welcher nochmal in der Datei Fonts01.java zu findenist, zeigt ein einfaches Beispiel und sollte nach diesen Erklarungen eigentlich rela-tiv einfach nachzuvollziehen sein. (siehe Abb. 5.4)

5.3 Fonts 122

Abbildung 5.4. Ausgabe des Programms Font01

class Font01Panel extends JPanel{private Font font1 ;private Font font2 ;

public Font01Panel ( ){// Ze i g t beim S ta r t einmal a l l e S c h r i f t e n in der Konsole anSt r ing [ ] f on t s =

GraphicsEnvironment . getLocalGraphicsEnvironment ( ) .getAvailableFontFamilyNames ( ) ;

for ( S t r ing s : f on t s ){System . out . p r i n t l n ( s ) ;

}

// E r s t e l l t neue Sch r i f t−Objek tef ont1 = new Font (

”Comic Sans MS” , Font .BOLD, 18 ) ;f ont2 = new Font (

”Lucida Console ” , Font . ITALIC , 1 5 ) ;}

public void paintComponent ( Graphics g ){Graphics2D g2d = ( Graphics2D ) g ;

// S t e l l t d i e e r s t e S c h r i f t e in und g i b t einen Text ausg2d . setFont ( font1 ) ;g2d . drawString (

”Comic Sans MS / S c h r i f t a r t : Fett / ” +” Sch r i f t g r o ß e 18” ,50 , 5 0 ) ;

// S t e l l t d i e zwe i t e S c h r i f t e in und g i b t einen Text ausg2d . setFont ( font2 ) ;

5.4 Transparenz 123

g2d . drawString (”Lucida Console / S c h r i f t a r t : Kursiv / ” +” Sch r i f t g r o ß e 15” ,50 , 100 ) ;

}}

5.4 Transparenz

Zusatzlich zu den bisher dargestellten grafischen Operationen bietet Java nochdie Moglichkeit grafische Elemente transparent darzustellen. Dazu wird die KlasseAlphaComposite verwendet. Um eine Instanz von dieser Klasse zu erhalten wirdnicht etwa ein Konstruktor aufgerufen, sondern es wird uber die Methode getIn-stance() eine Instanz angefordert. Als Argumente bekommt diese Methode die Artder Transparenz ubergeben, sowie der Alphawert.

Es gibt verschiedene Arten der Transparenz:

• CLEAR: Weder die Farbe noch der Alphawert werden verwendet.• SRC: Die Quellfarbe wird als Zielfarbe ubernommen.• DST: Die Zielfarbe wird ubernommen.• SRC OVER: Die Quellfarbe wird uber die Zielfarbe gemischt.• DST OVER: Die Zielfarbe wird uber die Quellfarbe gemischt. Das Ergebnis

wird als Zielfarbe ubernommen.• SRC IN: Der Teil der Quelle, welcher innerhalb des Ziels liegt ersetzt das Ziel.• DST IN: Der Teil des Ziels, welcher innerhalb der Quelle liegt ersetzt das Ziel.• SRC OUT: Der Teil der Quelle, welcher außerhalb des Ziels liegt ersetzt das

Ziel.• DST OUT: Der Teil des Ziels, welcher außerhalb dees Ziels liegt ersetzt das

Ziel.• SRC ATOP: Der Teil der Quelle, welcher innerhalb des Ziels liegt wird mit dem

Ziel vermischt.• DST ATOP: Der Teil des Ziels, welcher innerhalb der Quelle liegt wird uber

die Quelle gemischt und ersetzt das Ziel.• XOR: Der Teil der Quelle, welcher außerhalb des Ziels liegt wird mit dem Ziel

kombiniert, welches außerhalb der Quelle liegt.

Zur Darstellung von Transparenz wird normalerweise der Parameter SRC OVERverwendet. Dabei wird von der Klasse AlphaComposite eine passende gemischteFarbe berechnet.Um diese gemischte Farbe berechnen zu konnen benotigt der Algorithmus nocheinen zweiten Parameter, welcher das Mischverhaltnis der beiden Farben an gibt.Dieses Mischverhaltnis wird als Alphawert ubergeben. Der Alphawert ist ein Float-Wert zwischen 0.0f und 1.0f, wobie bei einem Wert von 0.0f die Hintergrundfarbezu 100% ubernommen wird und die zweite Farbe verworfen wird. Andersherum

5.4 Transparenz 124

wird dann naturlich bei einem Wert von 1.0f die zweite Farbe zu 100% ubernom-men.

Hat man nun eine Instanz von AlphaComposite erhalten kann man diese mit derMethode Graphics2D.setComposite(Composite) an das Graphics2D-Objekt uber-geben und dafur sorgen, dass alle nachfolgenden grafischen Elemente mit diesenEinstellungen gezeichnet werden.

Der folgende Quellcode ist ein Teil der Datei graphics2D.Transparency01.javaund zeigt wie man eine solche AlphaComposite-Instanz verwenden kann. (sieheauch Abb. 5.5)

Abbildung 5.5. Ausgabe des Programms Transparency01

5.4 Transparenz 125

public void paintComponent ( Graphics g ){// Uberschre iben des a l t e n B i l d e ssuper . paintComponent ( g ) ;

/∗∗ Das ubergebene Graphics−Objekt i s t∗ genau genommen ein Graphics2D Objekt∗ und muss de sha l b nur g e c a s t e t werden .∗/

Graphics2D g2d = ( Graphics2D ) g ;

// E r s t e l l e n zwe ier KreiseArc2D . Double c i r c l e 1 =

new Arc2D . Double (20 ,20 ,100 ,100 ,0 ,360 ,Arc2D . Double .CHORD) ;

Arc2D . Double c i r c l e 2 =new Arc2D . Double (

40 ,40 ,60 ,60 ,0 ,360 ,Arc2D . Double .CHORD) ;

// E r s t e l l e n e ine s AlphaComposite Ob jek t sf loat alpha = 0 .5 f ;AlphaComposite ac =

AlphaComposite . g e t In s tance (AlphaComposite .SRC OVER, alpha ) ;

//Zeichnen der Kreiseg2d . s e tCo lo r ( Color .RED) ;g2d . f i l l ( c i r c l e 1 ) ;

// E i n s t e l l e n der Transparenzg2d . setComposite ( ac ) ;g2d . s e tCo lo r ( Color .YELLOW) ;g2d . f i l l ( c i r c l e 2 ) ;

}

Es ist also relativ einfach eine Transparenz darzustellen. Am besten ist es wohl,etwas mit den Ubergabeparametern der Methode AlphaComposite.getInstance(int,float)herumzuprobieren um ein besseres Verstandnis fur die einzelnen Einstellungen zuerhalten.

Weitere Informationen zu Java2D findet man auf der Homepage von Sun indem Java2D-Tutorial ([Java]).

6

2D-Grafik in C#

6.1 Einfuhrung

Zweidimensionale Grafik wird vielen Bereichen der Softwareentwicklung zur Vi-sualisierung eingesetzt. Daher enthalten die meisten aktuellen Hochsprachen wieJava und C# entsprechende APIs, die umfangreiche und performante Funktions-sammlungen zur Erzeugung von 2D-Grafik ermoglichen. In C# kommt zu diesemZweck die API GDI+ (Graphics Device Interface) zum Einsatz, die Zugriff auf dieGrafikfunktionen von Windows bietet. Die wichtigsten Bereiche, die durch GDI+abgedeckt werden, sind

• 2D-Vektorgrafik• Darstellung und Manipulation von Bildern• Typographie

Weiterhin bietet GDI+ beispielsweise Funktionen zur Ausgabe von Text und Grafi-ken auf Druckern. Die folgenden Kapitel konzentrieren sich auf die drei o.g. Haupt-kategorien.

Anmerkung: In den folgenden Kapiteln verwenden wir der Einfachheit halberden GUI-Designer von Visual Studio, um die benotigten Controls und Formszu entwerfen. Die verwendeten Elemente und ihre Funktionsweise werden imGrundlagen-Kapitel naher erlautert.

6.2 2D-Grafik in C#

6.2.1 Initialisierung des Grafikkontexts

Das folgende Beispielprogramm demonstriert die grundlegende Intialisierung desGrafikkontexts in C#. Wir erzeugen zunachst eine neue Anwendung vom Typ

”Windows Form“ im aktuellen Projekt (siehe Abb. 5.1).

6.2 2D-Grafik in C# 127

Abbildung 6.1. Neue Windows Form

Um nicht fur jede erstellte Klasse eine eigene Main-Methode schreiben zumussen, erzeugen wir zusatzlich die Klasse

”startup“, die nur eine statische Main-

Methode enthalt. Um eine neu erstellte Klasse zu starten, muss nun lediglich derKlassenname in der Anweisung Application.Run() eingetragen werden.

namespace csharp 2d{

c l a s s s ta r tup{stat ic void Main ( ){

Appl i cat ion .Run(new Graphics2d1 ( ) ) ;}

}}

6.2 2D-Grafik in C# 128

6.2.2 Beispiel 1 - Die erste 2D-Grafik

Die wichtigste Klasse zur Erzeugung von 2D-Grafik mit GDI+ ist Graphics, dasie sowohl die sog. Grafikoberflache als auch elementare Zeichenfunktionen zurVerfugung stellt. Da die Referenz auf den Grafikkontext mehrmals im Programmbenotigt wird, erstellen wir eine Referenzvariable, die spater Zugriff auf den Kon-text erlaubt:

pub l i c p a r t i a l c l a s s Graphics2d1 : Form{Graphics g ;Pen myPen ;. . .

Einige der 2D-Funktionen sind in einem eigenen namespace abgelegt. Daherfugen wir folgende using-Anweisung hinzu, damit diese Funktionen ohne Angabedes kompletten namespaces verwendet werden konnen.

us ing System . Drawing . Drawing2D ;

Im Konstruktor der Klasse erzeugen wir anschließend mit der Methode Create-Graphics() einen Grafikkontext fur die Hauptform des Programms.

pub l i c Graphics2d1 ( ){

In i t i a l i z eComponent ( ) ;t h i s . Text = ”2D Graf ik mit C# − Be i s p i e l 1” ;

g = th i s . CreateGraphics ( ) ;myPen = new Pen( Color . Red ) ;g . SmoothingMode = SmoothingMode . Ant iAl ias ;

}

Um auf dem erzeugten Grafikkontext zeichnen zu konnen, benotigen wir zusatz-lich ein Objekt der Klasse Pen. Unter einem Pen kann man sich im weitesten Sinneeinen Stift vorstellen, dessen Eigenschaften wie Farbe, Strichbreite etc. vom Ent-wickler verandert werden konnen. Mit Hilfe der Eigenschaft SmoothingMode weisenwir GDI+ an, beim Zeichnen Antialiasing (Kantenglattung) durchzufuhren. Da-durch wird der sog. Treppeneffekt vermindert, die gezeichneten Formen erscheinenglatter.

Da die erstellte Grafik bei jeder Veranderung des Fensters (Minimierung, Ande-rung der Große, etc.) neu gezeichnet werden muss, ist sinnvoll, alle Zeichenfunk-tionen in der OnPaint-Methode der Form zu implementieren. Zu diesem Zweckmussen wir die Methode mit dem Schlusselwort

”override“ uberschreiben:

pro tec ted ove r r i d e void OnPaint ( PaintEventArgs e ){g . DrawLine (myPen , 10 , 10 , 200 , 200 ) ;

}

6.2 2D-Grafik in C# 129

Hier verwenden wir die DrawLine-Methode, um eine Linie auf die Form zu zeich-nen.

Abbildung 6.2. Beispielprogramm 1

6.2 2D-Grafik in C# 130

6.2.3 Beispiel 2 - Ellipsen und Rechtecke

Neben einfachen Linien konnen auch komplexere Formen wie Ellipsen und Recht-ecke gezeichnet werden. Die Methodik ist bei diesen geometrischen Formen gleich.Wir definieren zunachst neben den im vorherigen Kapitel beschriebenen Initia-lisierungen ein Objekt der Klasse Rectangle, daß die Position und Ausmaße derFiguren angibt:

pub l i c p a r t i a l c l a s s Graphics2d2 : Form{Graphics g ;Pen myPen ;

Rectangle figurRahmen = new Rectangle (50 , 50 , 200 , 300 ) ;. . .

Die ersten beiden Werte geben die Koordinaten der linken Oberen Ecke des Recht-ecks an, die anderen Werte beschreiben Breite und Hohe. Mit Hilfe dieses Rechteckskonnen nun die gewunschten Figuren gezeichnet werden:

pro tec ted ove r r i d e void OnPaint ( PaintEventArgs e ){g . DrawEll ipse (myPen , figurRahmen ) ;g . DrawRectangle (myPen , figurRahmen ) ;

}

6.2 2D-Grafik in C# 131

Abbildung 6.3. Beispielprogramm 2

6.2.4 Beispiel 3 - Brushes

Mit Hilfe von Brushes (Pinseln) ist es moglich, geschlossene geometrische Figurenverschiedenartig zu fullen. Neben einfarbigen Fullungen bietet C# eine Vielzahl anfertigen Fullmustern an. In diesem Beispiel sollen die Moglichkeiten von Brushesgezeigt werden. Im folgenden Kapitel werden drei verschiedene Brushes implemen-tiert:

• SolidBrush einfarbige Fullung• HatchBrush Musterfullung• LinearGradientBrush zweifarbige Fullung mit fliessendem Ubergang

6.2 2D-Grafik in C# 132

Mit diesen Brushes sollen drei verschiedene Figuren gefullt werden, zwei Kreiseund ein Rechteck.

pub l i c p a r t i a l c l a s s Graphics2d3 : Form{Graphics g ;HatchBrush myHatchBrush ;Sol idBrush mySolidBrush ;LinearGradientBrush myGradientBrush ;

Rectangle e l l i p s e 1Re c t = new Rectangle (50 , 50 , 200 , 2 0 0 ) ; ;Rectangle e l l i p s e 2Re c t = new Rectangle (150 , 350 , 200 , 2 0 0 ) ; ;Rectangle rec tRect = new Rectangle (300 , 50 , 200 , 200 ) ;. . .

Die Initialisierung der Brushes hangt vom jeweiligen Brush-Typ ab:

pub l i c Graphics2d3 ( ){

. . .mySolidBrush = new Sol idBrush ( Color . Green ) ;myHatchBrush = new HatchBrush ( HatchStyle . Cross ,

Color . Yellow , Color . Green ) ;

myGradientBrush = new LinearGradientBrush ( e l l i p s e 2Rec t ,Color . Yellow , Color . Green , LinearGradientMode . ForwardDiagonal ) ;

. . .

Beim SolidBrush muss lediglich die gewunschte Fullfarbe angegeben werden. DerHatchBrush benotigt zur Initialisierung das Fullmuster (wir entscheiden uns furein Karomuster) und eine Vordergrund- und Hintergrundfarbe. Der LinearGra-dientBrush benotigt das umschließende Rechteck des zu fullenden Objekts, diebeiden Farben und die Art des Farbverlaufs (in diesem Fall entscheiden wir unsfur einen diagonalen Farbverlauf). In der OnPaint-Methode verwenden wir nun dieentsprechenden fill-Methoden, statt die im vorherigen Kapitel besprochene draw-Methoden:

pro tec ted ove r r i d e void OnPaint ( PaintEventArgs e ){g . F i l l E l l i p s e ( mySolidBrush , e l l i p s e 1Re c t ) ;g . F i l l E l l i p s e ( myGradientBrush , e l l i p s e 2Re c t ) ;g . F i l lRe c t ang l e (myHatchBrush , rec tRect ) ;

}

6.2 2D-Grafik in C# 133

Abbildung 6.4. Beispielprogramm 3

6.2.5 Beispiel 4 - Polygone

Mit Hilfe von Polygonen konnen verschiedenste Formen auf recht einfache Artund Weise erzeugt werden. Im folgenden Kapitel soll ein kurzer Uberblick uber dieErzeugung von Polygonen mit GDI+ gegeben werden. Um ein Polygon darstellenzu konnen, benotigen wir neben den bereits bekannten Grafikobjekten Graphicsund Pen ein Array von Koordinaten, das die Eckpunkte des Polygons beschreibt:

pub l i c p a r t i a l c l a s s Graphics2d4 : Form{

. . .Point [ ] myPoints1 = {new Point (50 , 50) ,

new Point (150 , 150) , new Point (130 , 160) } ;. . .

6.2 2D-Grafik in C# 134

Mit Hilfe der Methode DrawPolygon kann das Polygon anschließend in derOnPaint-Methode gezeichnet werden:

pro tec ted ove r r i d e void OnPaint ( PaintEventArgs e ){g . DrawPolygon (myPenBlau , myPoints1 ) ;. . .

}

Abbildung 6.5. Beispiel 4

6.2 2D-Grafik in C# 135

6.2.6 Beispiel 5 - GraphicsPath

Die Klasse GraphicsPath bietet eine Vielzahl von Moglichkeiten, um komplexeVektorgrafiken aus verschiedenen Elementen und geometrischen Figuren zu erzeu-gen. In den folgenden Kapiteln wird der GraphicsPath vor allem dazu verwendet,Kurven zu zeichnen, deren Punkte zur Laufzeit berechnet werden. Sie werden derReihe nach dem GraphicsPath hinzugefugt, abschließend wird der komplette Gra-phicsPath gezeichnet. Der folgende Codeausschnitt zeigt die Moglichkeiten vonGraphicsPath:

myGraphicsPath . AddLine (0 , 0 , 30 , 2 0 ) ;myGraphicsPath . AddEl l ipse (20 , 20 , 20 , 4 0 ) ;myGraphicsPath . AddBezier (30 , 60 , 70 , 60 , 50 , 30 , 100 , 1 0 ) ;myGraphics . DrawPath (myPen , myGraphicsPath ) ;

Abbildung 6.6. GraphicsPath

6.2 2D-Grafik in C# 136

6.2.7 Beispiel 6 - Transformationen

Vektorgrafiken konnen mit Hilfe von Transformationsmatrizen skaliert, rotiert undverschoben werden. C# enthalt bereits Funktionen zur Transformation von Vek-torgrafiken. In diesem Kapitel soll exemplarisch die Rotation eines GraphicsPathgezeigt werden. Wir verwenden 2 Matrizen fur die benotigten Transformationen:eine Translationsmatrix zur Verschiebung der Figur aus dem Ursprung und eineRotationsmatrix.

Matrix myRotationMatrix = new Matrix ( ) ;Matrix myTranslationMatrix = new Matrix ( ) ;Graphics g ;GraphicsPath gp = new GraphicsPath ( ) ;GraphicsPath oldPath ;int winkel ;

Da wir absolute Transformationen durchfuhren, benotigen wir ein BufferobjektoldPath, das den ursprunglichen GraphicsPath enthalt. Die Variable winkel enthaltdie Gradzahl der aktuellen Drehung. Im Konstruktor initialisieren wir die Trans-lationsMatrix und verschieben das erzeugte Rechteck aus dem Ursprung, damit eskorrekt in der Mitte des Fensters angezeigt wird.

pub l i c Graphics2d5 ( ){

. . .gp . AddRectangle (new Rectangle (−100 , −100, 200 , 2 0 0 ) ) ;oldPath = ( GraphicsPath ) gp . Clone ( ) ;

myTranslationMatrix . Trans late (150 , 150 ) ;gp . Transform ( myTranslationMatrix ) ;

winke l = 0 ;}

Die OnPaint-Methode enthalt lediglich den Funktionsaufruf zum Zeichnendes GraphicsPath. Die Rotationen werden in den Button Click-Methoden durch-gefuhrt:

p r i va t e void button1 Cl i ck ( ob j e c t sender , EventArgs e ){gp = ( GraphicsPath ) oldPath . Clone ( ) ;

winke l += 5 ;

myRotationMatrix . Reset ( ) ;myRotationMatrix . Rotate ( winke l ) ;

gp . Transform ( myRotationMatrix ) ;

6.2 2D-Grafik in C# 137

gp . Transform ( myTranslationMatrix ) ;

Refresh ( ) ;}

Als erster Schritt wird die Rotationsmatrix auf die Einheitsmatrix zuruckgesetzt.Anschließend wird die Rotation mit dem aktuellen Winkel durchgefuhrt. Die Rei-henfolge der Rotation und Translation ist wichtig: wurde zuerst die Translationdurchgefuhrt, wurde sich das Rechteck nicht wie gewunscht um seine eigene Achse,sondern um den Ursprung des Fensters drehen. Die Implementierung des zweitenButtons ist bis auf die Anderung des Winkels aquivalent.

Abbildung 6.7. Rotation eines GraphicsPath

6.2 2D-Grafik in C# 138

6.2.8 Einfuhrung Splines

Unter dem Begriff Splines versteht man Kurven, die durch mehrere Polynomeschrittweise beschrieben werden. Da sie die Approximation von komplexen Kurvenerlauben und trotzdem relativ einfach zu implementieren sind. In den folgendenKapiteln werden verschiedene Typen von Splines vorgestellt und implementiert.

Abbildung 6.8. Bezier-Kurve

6.2.9 Splines 1 - Hermite-Splines

Kubische Hermite-Kurven (benannt nach dem franzosischen Mathematiker CharlesHermite) bestehen aus zwei Kontroll- (Start- und Endpunkt: p0, p1) und zwei Tan-gentenpunkten (m0, m1). Alle Punkte der Kurve werden mittels folgender Formelinterpoliert:

p(t) = (2t3 − 3t2 + 1)p0 + (t3 − 2t2 + t)m0 + (−2t3 + 3t2)p1 + (t3 − t2)m1

Zur ubersichtlicheren Implementierung erstellen wir fur jedes Polynom eine eigeneFunktion (die Polynome wurden aus Effizienzgrunden etwas umgestellt):

p r i va t e double f 0 (double u) {return 2∗u∗u∗u − 3∗u∗u + 1 ;

}

pr i va t e double f 1 (double u) {return (u∗u∗u)−(2∗u∗u)+u ;

}

pr i va t e double f 2 (double u) {return (−2∗(u∗u∗u ) ) + (3∗u∗u ) ;

}

6.2 2D-Grafik in C# 139

pr i va t e double f 3 (double u) {return (u∗u∗u) − (u∗u ∗ ) ;

}

Mit Hilfe einer Schleife erzeugen wir die Punkte der Kurve. Sie werden in einenGraphicsPath als Linien ubernommen. Abschließend wird der GraphicsPath ge-zeichnet, der nach Durchlauf der Schleife die komplette Kurve enthalt.

p ro tec ted ove r r i d e void OnPaint ( PaintEventArgs e ){Point o ldPoint = p0 ;pointPen . Width = 3 ;

for (double i = 0 ; i <= 1 ; i += 0 .001 ){double Px = f0 ( i ) ∗ p0 .X + f1 ( i ) ∗ m0.X + f2 ( i ) ∗

p1 .X + f3 ( i ) ∗ m1.X;

double Py = f0 ( i ) ∗ p0 .Y + f1 ( i ) ∗ m0.Y + f2 ( i ) ∗p1 .Y + f3 ( i ) ∗ m1.Y;

Point aktPoint = new Point ( ( int )Px , ( int )Py ) ;gp . AddLine ( oldPoint , aktPoint ) ;o ldPoint = aktPoint ;

}. . .g . DrawPath (myPen , gp ) ;

6.2 2D-Grafik in C# 140

Abbildung 6.9. Hermite-Kurve

6.2.10 Splines 2 - Bezier-Splines

C# enthalt zwar eine Funktion zum Erzeugen von Bezier-Kurven, wir wollen al-lerdings an dieser Stelle eine eigene Implementierung vornehmen. Bezier-Kurvenwurden von dem franzosischen Ingenieur Pierre Bezier zum Einsatz im Automobil-design entwickelt. Genau wie die Hermitekurve besteht die Bezier-Kurve aus vierPunkten. Allerdings kommen andere Polynome zum Einsatz, die der Bezier-Kurveeine andere Charakteristik verleihen:

b0(u) = (1 − u)3

b1(u) = 3u ∗ (1 − u)2

b2(u) = u3

b3(u) = 3u2 ∗ (1 − u)

6.2 2D-Grafik in C# 141

Wir erstellen wieder die entsprechenden Funktionen:

p r i va t e double b0 (double u){return Math .Pow((1 − u ) , 3 ) ;

}

pr i va t e double b1 (double u){return 3 ∗ u ∗ Math .Pow((1 − u ) , 2 ) ;

}

pr i va t e double b2 (double u){return Math .Pow(u , 3 ) ;

}

pr i va t e double b3 (double u){return (3 ∗ u ∗ u ∗ (1 − u ) ) ;

}

Der Aufbau der OnPaint-Methode ist aquivalent zum Hermiteprogramm. Als ein-zige Anderung mussen die o.g. Funktionen in die Formeln ubernommen werden:

. . .double Px = b0 ( i ) ∗ p0 .X + b1 ( i ) ∗ m0.X + b2 ( i ) ∗

p1 .X + b3 ( i ) ∗ m1.X;

double Py = b0 ( i ) ∗ p0 .Y + b1 ( i ) ∗ m0.Y + b2 ( i ) ∗p1 .Y + b3 ( i ) ∗ m1.Y;

. . .

6.2 2D-Grafik in C# 142

Abbildung 6.10. Bezierkurve

6.2.11 Splines 3 - Cardinal Splines

Cardinal Splines sind spezielle Hermite Kurven, deren Tangenten durch Punkteund Tensions (engl. tension = Spannung) definiert sind. Die Krummung der Kur-ve ergibt sich durch Angabe eines Tensionwerts. Ein kleiner Tensionwert ergibteine hohere Spannung der Kurve, die Verbindungen zwischen den Punkten werdendadurch weniger rund (siehe Abbildung).

Abbildung 6.11. Auswirkung des Tensionwerts

Um die Auswirkungen der Tension auf die Kurve zu verdeutlichen, soll das fol-gende Programm eine Cardinal Spline mit variabler Tension zeichnen. Die Tensionkann dann uber einen Schieberegler verandert werden. Wir implementieren dieCardinal Splines mit Hilfe der C#-Funktion DrawCurve. Die Punkte der Kurvewerden im Programm vorgegeben, die Tension-Variable enthalt einen Initialwertvon 0.0, der zur Laufzeit vom Benutzer verandert werden kann:

6.2 2D-Grafik in C# 143

pub l i c p a r t i a l c l a s s Graph i c s2dsp l ine s3 : Form{Graphics g ;Pen myPenBlau = new Pen( Color . Blue ) ;Pen myPenRot = new Pen( Color . Red ) ;f loat t en s i on = 0 .0F ;

Point [ ] myPoints = { new Point (50 , 50) , new Point (130 , 100) ,new Point (130 , 160) , new Point (230 , 120) ,new Point (10 , 10) } ;

. . .

Neben den bereits bekannten Initialisierungen der Pens und des Grafikkontextslegen wir im Konstruktor der Klasse den Wertebereich und den Abstand der gra-fischen Markierungen des Schiebereglers (trackBar1) fest. Hierbei ist zu beachten,daß der Schieberegler nur ganzzahlige Werte zuruckgibt. Der Schieberegler wurdeim GUI-Designer erstellt, daher befindet sich die Deklaration von trackBar1 imautomatisch generierten Teil der Klasse.

pub l i c Graph i c s2dsp l ine s3 ( ){

In i t i a l i z eComponent ( ) ;t h i s . Text = ”2D Graf ik mit C# − Cardinal Sp l i n e s ” ;

g = th i s . CreateGraphics ( ) ;myPenRot . Width = 2 ;

t h i s . trackBar1 .Maximum = 50 ;t h i s . trackBar1 .Minimum = 0 ;t h i s . trackBar1 . TickFrequency = 10 ;

}Die OnPaint-Methode zeichnet neben Markierungen der Punkte die Kurve mit

Hilfe der C#-Funktion DrawCurve. Diese Methode enthalt mehrere Uberladungen;bei Ubergabe der drei unten angegebenen Parameter wird eine Cardinal Splineerzeugt.

p ro tec ted ove r r i d e void OnPaint ( PaintEventArgs e ){g . DrawCurve (myPenBlau , myPoints , t en s i on ) ;. . .

Als letzten Schritt mussen wir Veranderungen am Schieberegler auswerten. Da-zu implementieren wir die Methode trackBar1 Scroll. Die Tension soll zwischen 0.0und 5.0 wahlbar sein, daher wahlen wir einen Wertebereich von 0 bis 50 und divi-dieren den eingestellten Wert durch 10. Das Label

”label1“ gibt den vom Benutzer

eingestellten Tensionwert aus.

6.2 2D-Grafik in C# 144

pr i va t e void t r a ckBa r1 Sc r o l l ( ob j e c t sender , EventArgs e ){

t h i s . t en s i on = ( f loat ) trackBar1 . Value / 10 .0F ;t h i s . l a b e l 1 . Text = ”Tension : ” + tens i on ;t h i s . Refresh ( ) ;

}

Abbildung 6.12. Cardinal Splines

6.3 Fonts in C# 145

6.3 Fonts in C#

Das folgende Kapitel zeigt die Verwendung von Fonts (Schriften) im Grafikkontext.GDI+ unterscheidet zwischen FontFamily und dem eigentlichen Font. FontFamilybeschreibt den allgemeinen Fontstil, bekannte Font Families sind Arial und Ti-mes New Roman. Der Font spezifiziert die Attribute der Schrift wie Schriftgroße,Kursiv- und Fettschrift.

Der folgende Codeauschnitt verdeutlicht die Unterschiede zwischen Font Familyund Font:

FontFamily myFontFamily = new FontFamily ( ” Ar i a l ” ) ;Font myFont = new Font (myFontFamily ,

16 ,FontStyle . Regular ,GraphicsUnit . P ixe l ) ;

Der so definierte Font kann nun verwendet werden, um mittels der DrawString-Methode einen Text auf der Form auszugeben:

pro tec ted ove r r i d e void OnPaint ( PaintEventArgs e ){< I n i t i a l i s i e r u n g des Fonts s . o.>. . .Point textPos = new Point (30 , 1 0 ) ;g . DrawString ( ”Hal lo Welt” , myFont , Brushes . Blue , textPos ) ;

}

Abbildung 6.13. Fonts in C#

6.4 Bitmaps in C# 146

6.4 Bitmaps in C#

C# bietet neben den in den vorherigen Kapiteln gezeigten Zeichen- und Fontme-thoden eine umfangreiche Funktionssammlung zur Anzeige und Manipulation vonBildern. In diesem Kapitel sollen die grundlegenden Moglichkeiten naher erlautertwerden. Die einfache Darstellung eines Bilds ist recht simpel und lasst sich mitwenigen Befehlen realisieren:

Bitmap myBitmap = new Bitmap ( ”sonnenblume .bmp” ) ;g . DrawImage (myBitmap , 10 , 1 0 ) ;

Die Klasse Bitmap unterstutzt neben dem jpg-Format auch die Formate BMP,GIF, JPEG, EXIF, PNG, TIFF, und ICON.

Abbildung 6.14. Bitmaps in C#

Literatur

DeL02. DeLoura, Marc: Spieleprogrammierung Gems 1. mitp, 2002.GHJV95. Gamma, Erich, Richard Helm, Ralph E. Johnson und John

Vlissides: Design Patterns: Elements of reusable object-oriented soft-ware. Addison-Wesley, 1995.

Java. 2D Graphics.2D Graphics, http://java.sun.com/docs/books/tutorial/2d/

index.html.Javb. A Visual Guide to Layout Managers.

A Visual Guide to Layout Managers, http://java.sun.com/docs/

books/tutorial/uiswing/layout/visual.html.Javc. API Java SE.

API zu Java SE, http://java.sun.com/j2se/1.5.0/docs/api/.SUNa. SUN: Java2D Tutorial.

Java2D Tutorial, http://java.sun.com/docs/books/tutorial/2d/

index.html.SUNb. SUN: Swing Tutorial.

Swing Tutorial, http://java.sun.com/docs/books/tutorial/ui/

index.html.