GUI 1 Die Notwendigkeit von GUIs 2 Die

Werbung
GUI
AnPr
Name
1
Klasse
Datum
Die Notwendigkeit von GUIs
Computerprogramme wurden erst dann für den „Laien“ interessant, als die Nutzerführung über eine grafische Nutzeroberfläche (englisch „Graphical User Interface“ – kurz GUI) realisiert wurde. Um dies zu ermöglichen, sind mitunter sehr aufwändige Programmierelemente zu realisieren. Da es für die meisten Programmierer jedoch zu zeitaufwändig (wenn überhaupt machbar) ist derartige Funktionen zu realisieren, greifen sie im Regelfall auf vorgefertigte Elemente in einer Programmbibliothek zurück, welche lediglich nur
noch „angepasst“ werden müssen. In Java gibt es mehrere Programmbibliotheken, die für uns nutzbar sind.
Hier die vier wichtigsten:
 AWT
 Swing
 SWT
 JavaFX
Jede dieser Bibliotheken hat seine Vor- und Nachteile. AWT und Swing sind Teil der JFC (also Java Foundation Classes) und sind somit auf allen Betriebssystemen gleich verfügbar. Wir müssen uns nicht um irgendwelche Abhängigkeiten Gedanken machen. AWT ist wiederum die Basis, auf der Swing aufsetzt. SWT
wurden von IBM für Eclipse entwickelt und hat mitunter ein ansprechenderes Design als Swing (wobei
Swing bezüglich „Look and Feel“ durchaus anpassbar ist). Nachteilig wirkt sich bei SWT aus, dass mitunter
Zusatzbibliotheken benötigt werden, welche nicht zwingend auf jedem System vorhanden sind. JavaFX ist
von Oracle die mittelfristig bevorzugte Lösung für Desktop Applikationen, wobei es jedoch von der Programmierkomplexität im Vergleich zu SWT und Swing aufwändiger ist.
In Bezug auf Einfachheit und Portierbarkeit bietet Swing im Zusammenspiel mit AWT derzeit die beste Lösung für unseren Unterricht. Aus diesem Grunde werden wir mit Swing arbeiten, wobei die grundsätzlichen
Konzepte im Wesentlichen allgemeingültig sind.
Wir finden die Swing Klassen in der Java Bibliothek „javax.swing“.
Info: Eine Klasse von Swing hatten wir bereits kennen gelernt – die JOptionPane Klasse. Hier haben wir die
Popup Fenster genutzt, um einfache Ein- und Ausgaben zu realisieren.
2
Die Grundstruktur
Ein Programm mit einer GUI hat typische Elemente. Hier die wichtigsten:
ANPR_GUI_Programmierung_v01.docx
Seite 1
GUI
AnPr
Jedes dieser Elemente findet ein Pendant in unserer Swing Bibliothek. Unser erstes Ziel ist es nun, diese
Elemente auszuprobieren, so dass wir das grundsätzliche Verhalten verstehen und nutzen können.
3
JFrame – oder der Rahmen unseres Programms
Alle Elemente werden auf den JFrame „montiert“. Doch bevor wir dies tun, probieren wir mal eine einfache
Anwendung aus:
Zuerst muss klar sein, dass wir hier nur einen kleinen „Testballon“ fliegen lassen. Unsere Applikation kann eigentlich noch gar
nichts! Das einzige, was hier passiert ist, dass ein Fenster geöffnet wird, in dessen Titel unser vorgesehener Text „Meine erste
GUI“ zu sehen ist. Das ist zwar nicht viel, aber immerhin der
erste Schritt.
Bleibt der Interpreter jetzt eigentlich stehen, wenn wir in der Zeile „myJFrame.setVisible(true);“ sind?
Antwort: So wie wir bis jetzt unseren Code gesehen haben, müsste man das denken. Tatsächlich ist es aber
so, dass Java einen eigenen „Thread“ für alle GUI Aktivitäten hat (also einen eigenen Prozess, der parallel zu
dem Prozess läuft, der unser Programm gestartet hat). Dieser sogenannte UI Thread übernimmt nun unser
Fenster. Somit läuft der Thread unseres Aufrufes ganz normal weiter und die main Methode wird beendet!
Unsere Variable „myJFrame“ repräsentiert nun das Fenster. Wir sind also nun darauf angewiesen, dass die
Klasse JFrame alles das bietet, was wir für unser Programm benötigen. Das ist zwar soweit OK, da wir recht
viel mit JFrame anstellen können, aber wir wollen noch flexibler werden. Deshalb gehen wir einen Schritt
weiter – wir erstellen unsere eigene JFrame Klasse, indem wir von JFrame erben.
Wie wir im Code sehen, erzeugen
wir uns eine eigene Klasse, welche
von JFrame erbt. Somit können wir
die JFrame Klasse um beliebig viele
Methoden ergänzen, so wie wir es
brauchen.
Weiterhin wurde in die MyJFrame
Klasse auch gleich die main Methode implementiert, so dass sich
die Klasse selbst aufrufen kann. Da
der JFrame ohnehin im UI Thread
läuft können wir dies so realisieren.
Nun müssen wir in unserem Code
noch etwas „aufräumen“. Es ist sinnvoll, dass die Dinge, welche für die Funktionalität der gesamten Klasse
benötigt werden, auch innerhalb der Klasse definiert werden und nicht wie im obigen Beispiel in der main
Seite 2
AnPr
GUI
Methode. Hierzu zählt in unserem kleinen Programm alles, was wir individuell setzen. Sehen wir uns vor
diesem Hintergrund mal folgendes Programm an:
Der Unterschied ist nun, dass wir alle notwendigen Einstellungen in eigenen Methoden kapseln.
Dies schafft Übersicht! Auch den Titel können
wir außerhalb des Superkonstruktors über die
Methode „setTitle“ setzen.
Bei GUI Implementierungen ist es wichtig, den
Überblick zu bewahren, da sie sehr schnell sehr
große Ausmaße annehmen. Programme mit einer
vierstelligen Anzahl von Codezeilen sind keine
Seltenheit. Gewöhnen Sie sich also an, alle Funktionalitäten in eine geeignete Methoden zu Codieren und die Methode sinnvoll zu benennen.
Anmerkung: Im Code wurde stets mit dem „this“
Schlüsselwort gearbeitet. Dies „Geschmackssache“, da wir „this“ eigentlich nur dann brauchen,
wenn wir lokal die gleichen Bezeichner haben als
auf Instanzlevel (so wie wir es oft bei Settern haben). Hier habe ich „this“ stets verwendet, um den Blick auf
den Zugriff auf Instanzvariablen und Instanzmethoden zu verdeutlichen. In der eigenen Implementierung
kann auf das „this“ aber auch verzichet werden.
4
Einfügen eines Elements
Elemente werden mit „add“ an das JFrame
angehängt. Hierbei können wir verschiedene
Elemente platzieren, welche weiter unten näher
beschrieben werden. Für unseren ersten Versuch verwenden wir ein „JLabel“, also ein
Textelement. Um möglichst übersichtlich zu
bleiben ist es eine gängige Praxis, die Platzierung der Elemente in eine eigene Methode auszulagern.
Wenn wir den Code nun ausführen, haben wir
unser erstes Element in den JFrame eingetragen und wir sehen den Text „hallo“ auf dem Bildschirm.
5
Das Layout
Wenn wir unseren Code um ein zweites JLabel erweitern, werden wir eine kleine Überraschung sehen. Unser
JFrame zeigt nun nur den letzten Text („welt“) an:
Das Problem das wir hier haben ist, dass wir noch kein Layout festgelegt haben. Eine zentrale Herausforderung bei GUI Anwendungen ist, dass wir zur Programmierzeit nicht immer festlegen können, welche Größe
der Bildschirm hat und somit was die sichtbare Fläche für unser Fenster ist. Insofern ist es sinnvoll, das
Fenster unseres JFrames von der Größe her änderbar zu machen. Dadurch können wir aber nicht mehr unsere
Elemente fix auf dem Fenster platzieren. Eine Lösung hierfür bieten die LayoutManager.
Seite 3
GUI
AnPr
5.1 Der Layout Manager
Die (dynamische) Platzierung unserer Elemente übernimmt der Layoutmanager von
Java. Bevor wir uns diesen ansehen, müssen
wir uns nochmal kurz mit dem JFrame beschäftigen. Dieser JFrame besteht eigentlich
aus mehreren Ebenen. Dies ist für die ersten
Applikationen zwar erstmal nicht superwichtig, aber wir müssen jetzt das Verhalten der
Inhaltsdarstellung ändern und das läuft nun
mal auf der Ebene der ContentPane.
Dort müssen wir einen LayoutManager platzieren, der sich um die Anordnung der Inhalte kümmert. Der Code hierzu würde wie
folgt aussehen:
Die große Frage ist nun, was mache ich jetzt mit
dem Layoutmanager eigentlich? Hier müssen wir
(leider) etwas weiter ausholen. Java bietet mir eine
Vielzahl von Layoutmanagern an. Hier eine kurze
Auflistung der Swing Layoutmanager:
 BorderLayout
 BoxLayout
 CardLayout
 GridBagLayout
 GridLayout
 GroupLayout
 SpringLayout
Es würde jetzt zu weit führen, alle einzelnen Layouts detailliert zu beschreiben. Unter folgendem Link können die Eigenheiten der Layoutmanager nachgelesen werden:
https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
An dieser Stelle werden wir uns auf
das GridBagLayout beschränken, da
es eine sehr flexible Lösung darstellt,
die fast alle Fälle abdeckt. Um das
Layout zu verstehen, müssen wir unseren Dialog etwas schematischer
sehen. Wir können ihn als eine Ansammlung von Spalten und Zeilen
(also „Zellen“) sehen, wobei es auch
Inhalte gibt, welche mehrere Felder
abdecken können.
Seite 4
AnPr
GUI
5.2 Die Konfiguration des Layout Managers
Wir haben jetzt eine Möglichkeit, die einzelnen Elemente zu benennen, da eine Ortsangabe möglich ist. Jedes dieser Elemente bekommt nun grafische
Eigenschaften, die sogenannten „Constraints“.
Dort wird festgelegt, wie sich die Zelle im Gesamtverbund verhalten soll. Jeder Zelle kann
über eine X- und Y-Koordinate identifiziert
werden. Dazu wird jedem Element mitgeteilt,
wo im GridBag es sich befindet. Weiterhin können wir noch festlegen, wie viel Prozent der
Höhe oder Breite das Element verwenden darf (Standard ist eine Gleichverteilung).
Wie gezeigt, setzen wir nun die einzelnen Elemente auf die X- und Y-Position und legen das „Gewicht“
fest. Setzen wir es bspw. auf 0.67, dann „versucht“ das Element 67% der Gesamtbreite für sich zu verwenden (sofern genügend Platz vorhanden ist). Wenn wir nun noch ein zusätzliches Label erstellen wollen, welches beide Spalten der ersten beiden hinweggeht, müssen wir einen
neuen Constraint setzen: „gridwidth“.
Wir dürfen nicht vergessen, dass der Wert für gridwidth für alle folgenden Elemente wieder zurückgesetzt werden muss (sofern wir für
jedes Element das Constraints Objekt wiederverwenden).
6
Die Elemente eines Frames
6.1 JLabel für Text
Textelemente werden als „Labels“ bezeichnet. In Swing erfolgt dies über die Klasse „JLabel“. Hier können
neben dem Textinhalt auch Textgröße und Textfarbe eingestellt werden:
Wenn wir den Code nun ausführen, sehen wir unseren (jetzt roten) Text.
Seite 5
GUI
AnPr
Bevor wir nun weitermachen, noch ein kleiner Hinweis auf die Klasse „Font“. Der hier genutzte Konstruktor
für den Font enthält drei Parameter. Gehen wir diese kurz durch:
Parameter:
Fontname
Fontstil
Fontgröße
Bedeutung:
Bezeichner für den Font. Wenn ein spezieller Font angegeben wird, muss er auch
auf dem System vorhanden sein! Insofern
bieten sich allgemeingültige „logische“
Fonts an (siehe Beispiel).
Beispiel:
DIALOG:
DIALOG_INPUT
SANS_SERIF
SERIF
MONOSPACED
Siehe Code oben.
Hier wird PLAIN, BOLD und ITALIC
(also normal, fett und kursiv) unterschieden.
Fontgröße in pts.
Siehe Code oben
6.2 JLabel für Bilder
Es gibt mehrere Möglichkeiten, ein Bild in ein JFrame einzutragen. Wir konzentrieren uns hier erstmal auf
das einfachste Verfahren – ein Bild direkt in ein JLabel einzufügen:
Das File kann direkt im Projekt eingetragen werden. Hierzu platzieren wir es
direkt im Rootverzeichnis unseres Eclipse Projektes. Alternativ kann man
auch ein eigenes Package verwenden, was als eigener Unterordner beim Öffnen angegeben werden muss.
Achten Sie hier immer auf Groß/Kleinschreibung, da das Programm ja eventuell auch unter Linux laufen könnte.
6.3 Eingabefelder
Eingaben werden mittels „JTextField“ gemacht. Sie können in vielerlei Hinsicht noch
konfiguriert werden (Schriftgröße- und Farbe, Hintergrundfarbe etc.), wobei wir uns
zuerst „nur“ auf die einfache Funktionsweise konzentrieren.
Seite 6
AnPr
GUI
Der Zugriff auf den eingegebenen Wert können wir über die Methode getText() ermöglicht. Dies werden wir
bei dem Kapitel für die Weiterverarbeitung näher betrachten.
6.4 Buttons
Mit die wichtigsten Elemente stellen Buttons dar. Sie werden über die Klasse
JButton erzeugt.
Neben einfachen Textwerten können
auch Grafiken eingefügt werden. Hierzu
kann mit „setIcon()“ ein ImageIcon eingesetzt werden. Dieser wird wie beim
Bild einfügen geladen.
Somit hätten wir nun die wichtigsten Elemente besprochen. Doch eine sehr wichtige Sache fehlt noch. Wir
müssen es irgendwie schaffen, auf die Ereignisse unserer Elemente (bspw. Button gedrückt, oder Text verändert) im Code reagieren…
7
Signale vom Betriebssystem
Unsere Usereingaben, egal ob mit Maus
oder Tastatur, werden erstmal vom Betriebssystem in Empfang genommen. Von
dort aus geht das Signal weiter an die einzelnen laufenden Programme, unter anderem auch die JVM (Java Virtual Machine).
Diese könnte nun das Signal weiterleiten.
Die große Frage ist aber, an wen eigentlich? Dies muss der Programmierer erstmal
festlegen, indem er sagt, welche Methoden
die JVM im Falle eines Klicks aufrufen
soll. Und schon sind wir beim zweiten
Problem angekommen. Woher soll die
JVM wissen, dass es eine Klasse von uns
gibt namens „MyJFrame“ und dann dort
auch noch eine Methode aufrufen, die wir geschrieben haben? Zum Zeitpunkt der Programmierung der JVM
gab es unsere Klasse ja noch gar nicht! Sehen wir uns hierzu das folgende Bild mal genauer an:
Seite 7
GUI
AnPr
Das Geheimnis zu diesem Problem heißt „Interface“. Ein Interface ist ein eigenes Programmierkonstrukt,
welches lediglich die Signatur von Methoden beinhaltet (ähnlich wie abstrakte Methoden). Sie stellt eine Art
„Vertrag“ dar, in dem steht, welche Methoden zu erwarten sind. Ein Interface kann man „implementieren“,
was nichts anderes bedeutet, dass die implementierende Klasse alle Methoden dieses „Vertrags“ auch implementiert.
In unserem Fall ist das Interface
„ActionListener“ und die zu implementierende Methode heißt
„actionPerformed(ActionEvent
arg0)“. Wenn ich der JVM jetzt
mitteile, dass meine Klasse
JFrame dieses Interface implementiert hat, so muss die JVM
nun davon ausgehen, dass auch
diese Methode existiert und kann
sie somit aufrufen. Sehen wir uns
den nebenstehenden Code nochmal Schritt für Schritt an.
So – nun haben wir es fast geschafft! das Einzige Problem ist
nun noch, dass wir bei der Auswertung des Ereignisses noch
nicht eindeutig feststellen können, wer das Event eigentlich ausgelöst hat. Wenn wir bspw. in unserem JFrame zwei Buttons platziert haben, möchten wir natürlich wissen, von wem das Event stammt. Hierzu müssen wir noch eine Kleinigkeit
anpassen:
Wir können mit der Methode „getSource()“ der Klasse „ActionEvent“ nun herausfinden, wer das Event eigentlich ausgelöst hat. Hierzu brauchen wir aber stets eine Referenz auf das Button Objekt. Aus diesem
Grunde dürfen wir es nicht als lokale Variable, sondern als Instanzvariable deklarieren. Achten sie darauf,
dass der Variablenname nun so aussagekräftig ist, dass wir immer wissen, um welchen Button es sich hier
handelt!
Wichtig: Grundsätzlich ist es notwendig, alle GUI Elemente mit denen wir noch weiterarbeiten müssen, in
Instanzvariablen abzulegen. Dies gilt für alle Elemente die Events auslösen, aber auch für Elemente, deren
Eigenschaften wir während der Laufzeit verarbeiten oder anpassen wollen (wie bspw. Text ändern, Text
auslesen, Grafiken anpassen usw.).
Seite 8
AnPr
8
GUI
Lizenz
Diese(s) Werk bzw. Inhalt von Maik Aicher (www.codeconcert.de) steht unter einer
Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 3.0 Unported Lizenz.
Seite 9
Herunterladen