Kurs:Java – ein schneller Einstieg/Eine grafische Benutzeroberfläche

Eine grafische Benutzeroberfläche Bearbeiten

Anwender werden von klar aufgebauten und intuitiv erfassbaren Oberflächen positiv motiviert. Diese Aussage ist so alt wie wahr. Es ist keinem Kunden zuzumuten, die Daten für seine Buchhaltung in die Kommandozeile einzugeben. Zu einer funktionierenden GUI gehört mehr, als Eingabe und Anzeige. Wie viel mehr, wird in den folgenden Abschnitten besprochen.

Aufbau eines Fensters Bearbeiten

Ein Fenster ist in Java natürlich als Klasse vorhanden. Es enthält die gewünschten Komponenten der Anwendung und entfernt sich selbst mit allen Bestandteilen aus dem System, wenn es geschlossen wird. So einfach sollte es sein, so einfach ist es aber nicht.

Für den Aufbau einer fensterorientierten Anwendung muss zunächst wieder der Rahmen für Applications, also für von der Konsole startbaren Anwendungen, herhalten. Natürlich erhält diese Klasse einen neuen Namen.

class MyFrame {
 public static void main( String[] args) {
 }
}

Die Klasse JFrame Bearbeiten

Nun muss ein Fenster her. Es ist jedoch ein besonderes Fenster, denn in ihm laufen alle sichtbaren Aktionen ab. Es ist also so etwas wie ein Rahmen um die beteiligten Komponenten. Die Klassenbezeichnung lautet daher auch Frame[1]. Um eine gefälligere Gestaltung des Fensters zu bekommen, wird statt „Frame“ die Klasse JFrame[2] verwendet (auf die Unterschiede wird noch eingegangen). Also wird jetzt in der Methode main ein Objekt der Klasse „JFrame“ gebildet. Die Referenz soll die Bezeichnung „frame“ tragen.

Bei „JFrame“ handelt es sich um eine Klasse, die in Java nicht zu den allgemein verfügbaren zählt. Klassen werden in Java in sogenannten „Packages“ (Paketen) unterteilt. Zusammengehörige Java-Klassen lassen sich dadurch besser verwalten. Die Klasse „JFrame“ ist im Paket „javax.swing“ untergebracht, das seinerseits ein Unterpaket von „javax“ darstellt. Wenn jetzt die Frage auftaucht, woher man das alles wissen und behalten soll, so lautet die Antwort: "überhaupt nicht!". Der ganze Pfad durch die Packages steht in der API-Dokumentation der Klasse ganz am Anfang. So steht denn auch „javax.swing“ in der Dokumentation von „JFrame“ ganz vorn. Vor jedem „JFrame“ braucht also nur „javax.swing“ gefolgt von einem Punkt zu stehen und es treten keine Probleme auf.

class MyFrame {
 public static void main( String[] args) {
 javax.swing.JFrame frame = new javax.swing.JFrame();
 }
}

Der Anwender erwartet eine Titelzeile mit informativem Inhalt. Die Klasse „JFrame“ hat hierfür von der Klasse „Frame“ eine Methode mit dem Namen setTitle[3] geerbt. Der zu übergebende Parameter ist ein String[4] und wird vom JFrame-Objekt als Eigenschaft übernommen. Mit der Methode „setTitle“ wird also die Titel-Eigenschaft des Objekts „frame“ geändert. Ein sinnvoller Text für den Titel wäre "Rabattberechnung". Also rein damit in den Quelltext:

class MyFrame {
 public static void main( String[] args) {
  javax.swing.JFrame frame = new javax.swing.JFrame();
  frame.setTitle( "Rabattberechnung");
 }
}
Aufgabe:
Öffnen der API-Doc mit dem HTML-Browser. Suchen der Klasse JFrame (Tipp: linke Navi-Leiste unter "all classes"). Anklicken und ein wenig "stöbern". Suchen nach einem Eintrag (Methode, Field oder ähnlich) mit Bezug auf Sichtbarkeit (visibility) - die Betonung liegt auf "sichtbar".

Wieder eine reine Übungsaufgabe. Sehr wichtig, um ein "Gefühl" für den Aufbau von API-Docs zu bekommen. Sollte kein Eintrag gefunden werden, hilft die "Suche mit dem Browser".

Die Eigenschaft sichtbar zu sein Bearbeiten

Java ist sehr konsequent bezüglich der Anzeige. Es existiert zwar jetzt ein Fenster, aber es wird nicht angezeigt. Das Fenster-Objekt „frame“ hat die Eigenschaft, unsichtbar zu sein. Genauer formuliert: "frame hat die Eigenschaft, nicht sichtbar zu sein". Dieser kleine, aber wichtige Unterschied in der Formulierung macht die Funktionsweise der folgenden Methode deutlich. Die Eigenschaft „visible“ (sichtbar) hat zwei Zustände, die das gesamte Fenster betreffen, „visible“ und das Gegenteil davon. Das Gegenteil von „visible“ ist natürlich nicht „invisible“, sondern einfach „not visible“. Variablen und damit eben auch Eigenschaften, die nur zwei Zustände annehmen können, werden boolsche Variablen genannt. Sie haben, wie int- und float-Variablen Werte. Allerdings handelt es sich jetzt nicht mehr um numerische, sondern um boolsche Werte. Bezogen auf die Eigenschaft „sichtbar“, bedeuten die Werte:

  • Fenster wird angezeigt: Es ist wahr, dass es sichtbar ist, oder sichtbar = wahr.
  • Fenster wird nicht angezeigt: Es ist nicht wahr, dass es sichtbar ist, oder sichtbar = falsch.

Die Eigenschaft „visible“ hat also entweder den Wert „true“ oder den Wert „false“. Natürlich soll das Fenster „frame“ angezeigt werden, weshalb die Methode zum Setzen dieser Eigenschaft aufgerufen wird.

class MyFrame {
 public static void main( String[] args) {
 javax.swing.JFrame frame = new javax.swing.JFrame();
  frame.setTitle( "Rabattberechnung");
  frame.setVisible( true);
 }
}

Die Ausführung dieses Programms ist ernüchternd, oder einfach enttäuschend. Das Fenster ist an Winzigkeit kaum zu unterbieten und ein Klick auf den "Entfernen"-Knopf führt zu weiteren Irritationen. Los wird man dieses Programm nur mit "Gewalt", also über den Task-Manager. Es fehlt einfach an Komfort.

Anzeigefelder Bearbeiten

Zunächst sollte dieses Fenster mit ein paar Komponenten ausgestattet werden, damit es überhaupt einen Inhalt hat. Die ursprüngliche Aufgabe enthielt Hinweise auf Anzeigefelder. Diese Felder sollen Text enthalten können und editierbar sein, so die Vermutung. Fields (Felder) und Text können vielleicht zu einer Bezeichnung wie TextField[5] führen, hinter der sich eine Klasse verbirgt. API-Doku durchforsten und die Klasse „TextField“ suchen. Bitte nicht einsetzen, die Anwendung arbeitet mit einem JFrame[2], also der erweiterten grafischen Oberfläche. Also nach JTextField[6] suchen und genau diese Klasse einsetzen. Unter „einsetzen“ ist gemeint, zunächst latente Objekte (Referenzen ohne Inhalt) in den Quelltext einzubinden. Zu diesem Zweck wird der bisherige Quelltext etwas übersichtlicher gestaltet.

class MyFrame {
 public static void main( String[] args) {
  javax.swing.JFrame frame = new javax.swing.JFrame();
  javax.swing.JTextField betragFeld  = null;
  javax.swing.JTextField rabattFeld  = null;
  javax.swing.JTextField ausgabeFeld = null;
 
  frame.setTitle( "Rabattberechnung");
  frame.setVisible( true);
 }
}

Erster Kontakt zum LayoutManagement Bearbeiten

Eigentlich sollen diese drei Felder in das Fenster integriert werden. Allerdings gibt es da einige Hürden zu überwinden.


  • Wie groß sollen die Felder sein?
  • Wie sollen die Felder positioniert werden?
  • Was soll mit der Positionierung geschehen, wenn sich die Größe des Fensters ändert?

Eine Möglichkeit wäre: Felderpositionen und -größen festlegen und JFrame-Window fixieren. Diese Option bleibt immer, führt jedoch zu Anwenderprotesten. Eine andere Möglichkeit besteht darin, die Felder dynamisch anzupassen. Die Flexibilität ist maximal, führt jedoch zu eine extrem aufwendigen Programmierung. Ein Mittelweg muss gefunden werden und existiert bei Java in Form sogenannter LayoutManager[7].

Objekte dieser Klassen können auf jeden Container[8] angewendet werden, somit auch auf das Objekt „frame“. Damit wäre jedoch die Einschränkung verbunden, das Layout an ein bestehendes Fenster zu binden – für Java-Programmierer eine geradezu diktatorische Einschränkung. Ein eigener Container wird mit eigenen Komponenten gefüllt, ein eigener LayoutManager wird eingesetzt und der fertige Container in das Fenster integriert.

Aufgabe:

Die API-Doc gibt Auskunft über die Vererbungen.

Ist die Klasse JFrame von JContainer abgeleitet?

Die Klasse BorderLayout Bearbeiten

Container[8] sind in ihrer Instanz als Panel[9] sehr flexibel, wie es zum Beispiel die Klasse Applet[10] zeigt, die eine Erweiterung von „Panel“ ist. Klar, es muss hier ein JPanel[11] sein, denn diese Anwendung basiert auf Klassen aus dem Paket „javax.swing“. Der Quelltext wird nun um ein „JPanel“-Objekt erweitert und mit einem BorderLayout[12]-Manager ausgestattet.

class MyFrame {
 public static void main( String[] args) {
  javax.swing.JFrame frame = new javax.swing.JFrame();
  javax.swing.JPanel panel = new javax.swing.JPanel();
  javax.swing.JTextField betragFeld  = null;
  javax.swing.JTextField rabattFeld  = null;
  javax.swing.JTextField ausgabeFeld = null;
  panel.setLayout( new java.awt.BorderLayout());
  frame.setTitle( "Rabattberechnung");
  frame.setVisible( true);
 }
}

Diesmal wurde ein neues Objekt einfach in die Argumentliste einer Methode "hineininstanziiert", ohne erst eine Referenz zu definieren. Diese Vorgehensweise bietet sich immer dann an, wenn Eigenschaften Objektreferenzen sind. Der Layoutmanager ist eine Eigenschaft des Containerobjekts „panel“. Weil die ursprüngliche Eigenschaft des Containers (wahrscheinlich) nicht ein „BorderLayout“ war, wird es einfach neu definiert.

Jetzt müssen die Textfelder mit existierenden Referenzen ausgestattet werden, also Referenzen auf JTextField[6]-Instanzen. Die Länge der Felder sollte gleich sein und damit auch bei größeren Beträgen die Übersicht bestehen bleibt, wird eine Länge von 7 bei der Instanziierung mit angegeben. Die ursprünglichen „null“-Referenzen werden einfach ersetzt.

class MyFrame {
 public static void main( String[] args) {
  javax.swing.JFrame frame = new javax.swing.JFrame();
  javax.swing.JPanel panel = new javax.swing.JPanel();
  javax.swing.JTextField betragFeld  = new javax.swing.JTextField( 7);
  javax.swing.JTextField rabattFeld  = new javax.swing.JTextField( 7);
  javax.swing.JTextField ausgabeFeld = new javax.swing.JTextField( 7);
  panel.setLayout( new java.awt.BorderLayout());
  frame.setTitle( "Rabattberechnung");
  frame.setVisible( true);
 }
}

„Border“ bedeutet bekanntlich „Grenze“ und diese ist nicht an Händigkeiten (links, rechts) sondern an Himmelsrichtungen (West, Ost) orientiert. Ein BorderLayout[12] stellt allerdings nur vier Grenzangaben (NORTH, SOUTH, WEST, EAST) bereit und eine Angabe für das eingegrenzte Gebiet (CENTER). Mit diesen Orientierungen können die zu integrierenden Komponenten nun verteilt werden.

Aufgabe:

JPanels sind Behälter (Container) für Anzeigeelemente (Components).

Ist die Klasse JPanel von JContainer abgeleitet? Ist die Klasse JTextField von Container abgeleitet (genau lesen!).

Komponenten in das Anwendungsfenster integrieren Bearbeiten

Eine Komponente wird in einen Container[8] mit der Containermethode add[13] integriert. Dabei ist die Komponente ein Argument und die Orientierung der Komponente ein weiteres. Die Orientierung ist aber eine Eigenschaft des Layoutmanagers der ohne Referenz im main-Programm einfach an den Container delegiert wurde. Wie also an die Himmelsrichtungen herankommen? Hier kommt eine Besonderheit zutage, die „statischen Variablen“. Diese Variablen sind zwar Bestandteil eines Objekts, werden aber nur einmal erzeugt. Dadurch wird eine Referenzierung über den Klassennamen möglich. Abschließend wird das Panel einfach zum zentralen Inhaltscontainer des Fensters erklärt. Das Fenster muss sich vor der Anzeige noch über die "bevorzugten" Ausmaße seiner Componenten informieren, um dann seinerseits eine entsprechende Größe anzunehmen. auch hierfür existiert eine Methode, die den Namen pack[14] trägt. Sie wird normalerweise unmittelbar vor der Anzeige aufgerufen.

class MyFrame {
 public static void main( String[] args) {
  javax.swing.JFrame frame = new javax.swing.JFrame();
  javax.swing.JPanel panel = new javax.swing.JPanel();
  javax.swing.JTextField betragFeld  = new javax.swing.JTextField( 7);
  javax.swing.JTextField rabattFeld  = new javax.swing.JTextField( 7);
  javax.swing.JTextField ausgabeFeld = new javax.swing.JTextField( 7);
  panel.setLayout( new java.awt.BorderLayout());
  panel.add( betragFeld,  java.awt.BorderLayout.NORTH);
  panel.add( rabattFeld,  java.awt.BorderLayout.CENTER);
  panel.add( ausgabeFeld, java.awt.BorderLayout.SOUTH);
  frame.setTitle( "Rabattberechnung");
  frame.setContentPane( panel);
  frame.pack();
  frame.setVisible( true);
 }
}

Das Programm funktioniert. Leider hat es noch einen kleinen Nachteil, man wird es nicht wieder los. Zwar verschwindet es aus der Anzeige, jedoch nicht aus dem System. Wenn es aus einem anderen Programm heraus gestartet wurde, erscheint es nicht einmal in der Task-Leiste, was zu unangenehmen Folgen führen kann.

Referenzen Bearbeiten

  1. Frame in der Java-API
  2. 2,0 2,1 JFrame in der Java-API
  3. Frame.setTitle in der Java-API
  4. String in der Java-API
  5. TextField in der Java-API
  6. 6,0 6,1 JTextField in der Java-API
  7. LayoutManager in der Java-API
  8. 8,0 8,1 8,2 Container in der Java-API
  9. Panel in der Java-API
  10. Applet in der Java-API
  11. JPanel in der Java-API
  12. 12,0 12,1 BorderLayout in der Java-API
  13. Container.add in der Java-API
  14. Window.pack in der Java-API