Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 36 - Swing: Container und Menüs

## 36.1 Hauptfenster

- 36.1 Hauptfenster
  - 36.1.1 JFrame
    - RootPane, LayeredPane und ContentPane
    - Dialogelemente plazieren
  - 36.1.2 JWindow
  - 36.1.3 JDialog
  - 36.1.4 JOptionPane
    - Anzeigen einer Nachricht
    - Eingabe einer Bestätigung
    - Eingabe von Daten
  - 36.1.5 JApplet
  - 36.1.6 JInternalFrame
    - Der Desktop
    - Die Kindfenster
    - Beispielprogramm

### 36.1.1 JFrame

Die Klasse `JFrame` ist die wichtigste Hauptfensterklasse in Swing. Sie ist aus `java.awt.Frame` abgeleitet und stellt ein Hauptfenster mit Rahmen, Systemmenü und Standardschaltflächen zur Verfügung. Ein `JFrame` kann ebenso einfach instanziert werden wie ein `Frame`:

    public JFrame()
    public JFrame(String title)
| javax.swing.JFrame

Der parameterlose Konstruktor erzeugt ein Hauptfenster mit einer leeren Titelzeile, der andere schreibt den übergebenen String hinein. Da `JFrame` aus `Frame` abgeleitet ist, stehen alle Methoden aus `Window`, `Container` und `Component` zur Verfügung. Ein einfaches Beispielprogramm mit einem leeren Hauptfenster, das sich über den Schließen-Button beenden läßt, können wir (analog zu den Beispielen in Kapitel 31) wie folgt realisieren:

    001 /* Listing3601.java */
    002
    003 import javax.swing.*;
    004 import java.awt.event.*;
    005
    006 public class Listing3601
    007 extends JFrame
    008 {
    009   public Listing3601()
    010   {
    011     super("Ein einfacher JFrame");
    012     addWindowListener(new WindowClosingAdapter(true));
    013   }
    014
    015   public static void main(String[] args)
    016   {
    017     Listing3601 wnd = new Listing3601();
    018     wnd.setLocation(100, 100);
    019     wnd.setSize(300, 200);
    020     wnd.setVisible(true);
    021   }
    022 }
| Listing3601.java

Listing 36.1: Verwendung der Klasse JFrame

Das Programm erzeugt ein Hauptfenster der Größe 300 * 200 an der Bildschirmposition (100, 100). In der Titelzeile steht "Ein einfacher JFrame", und das Programm läßt sich durch einen Klick auf den Schließen-Button beenden:

Abbildung 36.1: Ein einfaches JFrame-Beispiel

#### RootPane, LayeredPane und ContentPane

Ein bedeutender Unterschied zwischen den AWT- und Swing-Hauptfenstern besteht in ihrer Komponentenstruktur und den sich daraus ergebenden Unterschieden in der Bedienung. Während die Komponenten eines AWT-Fensters direkt auf dem Fenster plaziert werden, besitzt ein Swing-Hauptfenster eine einzige Hauptkomponente, die alle anderen Komponenten aufnimmt.

Diese Hauptkomponente wird als RootPane bezeichnet und ist vom Typ `JRootPane`. Sie übernimmt die Rolle einer Art Verwaltungsinstanz für alle anderen Komponenten des Hauptfensters. Eine RootPane enthält folgende Komponenten:

- eine aus `Container` abgeleitete GlassPane und
- eine aus `JLayeredPane` abgeleitete LayeredPane

Die LayeredPane enthält ihrerseits zwei Unterkomponenten:

- eine aus Container abgeleitete ContentPane und
- eine aus `JMenuBar` abgeleitete Menüleiste

Damit ergibt sich folgende Struktur:

Abbildung 36.2: Die Struktur einer RootPane

LayeredPane und GlassPane liegen "übereinander" und füllen das Fenster jeweils komplett aus. Die GlassPane ist normalerweise durchsichtig und wird meist nicht zur Grafikausgabe benutzt. Sie könnte dann verwendet werden, wenn Effekte erzielt werden sollen, die das Fenster als Ganzes betreffen (und nicht seine einzelnen Dialogelemente). Eine (beispielsweise von `JInternalFrame` genutzte) Funktion besteht darin, Mausereignisse abzufangen, bevor sie an andere Komponenten weitergegeben werden.

Die LayeredPane enthält das Menü und die Dialogelemente der Anwendung. Als Instanz der Klasse `JLayeredPane` verfügt sie über die Fähigkeit, Dialogelemente nicht nur neben-, sondern in kontrollierter Weise auch übereinander anzuordnen. Das ist beispielsweise wichtig, um Menüs oder interne Dialoge über den Komponenten anzuzeigen, die sie verdecken. Tatsächlich verdeckt das in der LayeredPane gehaltene Menü die in seiner ContentPane plazierten Dialogelemente der Anwendung.

Das hört sich alles sehr kompliziert an, und mancher wird sich fragen, ob soviel Aufwand wirklich nötig war. Glücklicherweise braucht eine Swing-Anwendung sich um die Details gar nicht zu kümmern. Einerseits wird die RootPane, und mit ihr die darin enthaltene GlassPane, LayeredPane und ContentPane, beim Anlegen des Fensters automatisch erzeugt (einzig die Menüleiste bleibt standardmäßig leer). Zweitens implementieren alle Hauptfenster das Interface `RootPaneContainer`, das den Zugriff auf die RootPane vereinfacht. Einige seiner Methoden sind:

    public JRootPane getRootPane()
    public Container getContentPane()
    public JLayeredPane getLayeredPane()
    public Component getGlassPane()
| javax.swing.RootPaneContainer

Um auf einem Hauptfenster Komponenten zu plazieren, ist es also nicht nötig, zunächst mit `getRootPane` die RootPane, dann mit `getLayeredPane` die LayeredPane und schließlich mit `getContentPane` die ContentPane zu beschaffen, sondern es kann direkt `getContentPane` aufgerufen werden. Neben den getter-Methoden gibt es auch setter-Methoden, mit denen der strukturelle Aufbau der RootPane vollständig verändert werden kann. Darauf wollen wir aber nicht weiter eingehen.

#### Dialogelemente plazieren

Das Einfügen und Anordnen von Dialogelementen auf einem Hauptfenster erfolgt also über dessen ContentPane. Die Aufrufe von `add` und `setLayout` werden damit nicht direkt auf dem Fenster ausgeführt, sondern auf dessen ContentPane, die über einen Aufruf von `getContentPane` beschafft werden kann.

Um das zu demonstrieren, wollen wir das vorige Beispiel um drei Buttons erweitern, die mit Hilfe eines `GridLayout` der Größe 3 * 1 angeordnet werden:

    001 /* Listing3602.java */
    002
    003 import javax.swing.*;
    004 import java.awt.*;
    005 import java.awt.event.*;
    006
    007 public class Listing3602
    008 extends JFrame
    009 {
    010   public Listing3602()
    011   {
    012     super("Ein einfacher JFrame");
    013     //WindowListener hinzufügen
    014     addWindowListener(new WindowClosingAdapter(true));
    015     //Layout setzen und Buttons hinzufügen
    016     Container contentPane = getContentPane();
    017     contentPane.setLayout(new GridLayout(3, 1));
    018     contentPane.add(new JButton("Button 1"));
    019     contentPane.add(new JButton("Button 2"));
    020     contentPane.add(new JButton("Button 3"));
    021   }
    022
    023   public static void main(String[] args)
    024   {
    025     Listing3602 wnd = new Listing3602();
    026     wnd.setLocation(100, 100);
    027     wnd.setSize(300, 200);
    028     wnd.setVisible(true);
    029   }
    030 }
| Listing3602.java

Listing 36.2: Anordnen von Dialogelementen in einem Hauptfenster

Das Programm hat nun folgendes Aussehen:

Abbildung 36.3: Ein Hauptfenster mit Dialogelementen

|  |  |  | Gerade am Anfang passiert es mitunter, daß `add` oder `setLayout` versehentlich direkt aus dem Hauptfenster aufgerufen werden. Um das zu verhindern, besitzen die Hauptfensterklassen einen Mechanismus, der in einem solchen Fall eine Ausnahme mit einem entsprechenden Warnhinweis auslöst. Dieser kann zwar mit der Methode `setRootPaneCheckingEnabled` deaktiviert werden, normalerweise ist das aber nicht zu empfehlen. |  |
Warnung

### 36.1.2 JWindow

Die zweite Hauptfensterklasse, die wir in diesem Kapitel vorstellen, ist `JWindow`. Sie ist aus `Window` abgeleitet und dient wie diese dazu, ein rahmenloses Fenster zu erzeugen, das an beliebiger Stelle und in beliebiger Größe auf dem Bildschirm plaziert werden kann. `JWindow` besitzt drei Konstruktoren:

    public JWindow()
    public JWindow(Frame owner)
    public JWindow(Window owner)
| javax.swing.JWindow

Ebenso wie `JFrame` besitzt auch `JWindow` eine RootPane mit der im vorigen Abschnitt beschriebenen Struktur. Dialogelemente und Layoutmanager werden also nicht direkt auf dem Fenster, sondern auf der ContentPane plaziert. Zur einfacheren Verwendung implementiert auch `JWindow` das Interface `RootPaneContainer`.

Als Beispiel für die Anwendung von `JWindow` wollen wir einen einfachen SplashScreen konstruieren, also ein Fenster, das nach dem Start eines Programmes angezeigt wird und nach dessen Initialisierung wieder entfernt wird. Dazu leiten wir eine Klasse `SplashScreen` von `JWindow` ab und plazieren darin ein Icon und einen Text. Der Name der Icondatei und der Text können an den Konstruktor übergeben werden. Das Programm soll jeweils ein Drittel der verfügbaren Höhe und Breite des Bildschirms belegen und das Icon und den Text zentriert darin anzeigen.

    001 /* SplashScreen.java */
    002
    003 import javax.swing.*;
    004 import javax.swing.border.*;
    005 import java.awt.*;
    006 import java.awt.event.*;
    007
    008 public class SplashScreen
    009 extends JWindow
    010 {
    011   public SplashScreen(String image, String text)
    012   {
    013     JPanel contentPane = new JPanel();
    014     contentPane.setLayout(new BorderLayout());
    015     Border bd1 = BorderFactory.createBevelBorder(
    016       BevelBorder.RAISED
    017     );
    018     Border bd2 = BorderFactory.createEtchedBorder();
    019     Border bd3 = BorderFactory.createCompoundBorder(bd1, bd2);
    020     ((JPanel)contentPane).setBorder(bd3);
    021     ImageIcon icon = new ImageIcon(image);
    022     contentPane.add(new JLabel(" ", JLabel.CENTER), BorderLayout.NORTH);
    023     contentPane.add(new JLabel(icon, JLabel.CENTER), BorderLayout.CENTER);
    024     contentPane.add(new JLabel(text, JLabel.CENTER), BorderLayout.SOUTH);
    025     setContentPane(contentPane);
    026   }
    027
    028   public void showFor(int millis)
    029   {
    030     Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
    031     setLocation(dim.width / 3, dim.height / 3);
    032     setSize(dim.width / 3, dim.height / 3);
    033     setVisible(true);
    034     try {
    035       Thread.sleep(millis);
    036     } catch (InterruptedException e) {
    037     }
    038     setVisible(false);
    039   }
    040
    041   public static void main(String[] args)
    042   {
    043     SplashScreen intro = new SplashScreen(
    044       "mine.gif",
    045       "(C) Copyright 2000, J. Krüger, All Rights Reserved"
    046     );
    047     intro.showFor(3000);
    048     System.exit(0);
    049   }
    050 }
| SplashScreen.java

Listing 36.3: Ein einfacher SplashScreen

Die Ausgabe des Programms sieht so aus:

Abbildung 36.4: Ein einfacher SplashScreen

Das Programm ist prinzipiell so aufgebaut wie die bisherigen Swing-Beispiele, zeigt aber bei näherem Hinsehen einige Besonderheiten. Zunächst definiert es einen eigenen ContentPane. In Zeile 013 wird dazu ein `JPanel` instanziert und mit einem `BorderLayout` versehen (der Standard-Layoutmanager von `JPanel` ist `FlowLayout`). Alle Dialogelemente werden auf diesem Panel plaziert. In Zeile 025 wird das Panel durch Aufruf von `setContentPane` als ContentPane des Fensters definiert.

Zum anderen zeigt das Programm ein paar zusätzliche Möglichkeiten, Umrandungen zu verwenden. Ab Zeile 015 werden mit Hilfe der Methoden `createBevelBorder` und `createEtchedBorder` der Klasse `BorderFactory` zwei unabhängige Umrandungen konstruiert. Durch Aufruf von `createCompoundBorder` werden sie zu einer neuen Umrandung zusammengefaßt und anschließend an den ContentPane übergeben.

### 36.1.3 JDialog

Neben einem oder mehreren langlebigen Hauptfenstern besitzt eine Anwendung meist auch Dialogfenster. Sie werden oft nur vorübergehend aufgerufen, um eine temporäre Kommunikation zwischen Anwender und Programm zu etablieren. Dialogfenster unterscheiden sich meist dadurch von Hauptfenstern, daß sie kein Menü und nur eingeschränkte Systemfunktionen besitzen. Zudem ist ihre Größe oft nicht veränderbar, und sie halten (als modale Fenster) während der eigenen Ausführung den Programmfluß im übrigen Programm an.

Mit der aus `Dialog` abgeleiteten Klasse `JDialog` stehen auch in Swing Dialogfenster zur Verfügung. Sie besitzen denselben strukturellen Aufbau wie `JFrame` und `JWindow` und implementieren ebenfalls das Interface `RootPaneContainer`. Auch hier erfolgt also das Hinzufügen und Anordnen von Komponenten nicht auf dem Fenster selbst, sondern auf seiner ContentPane. `JDialog` besitzt eine Vielzahl von Konstruktoren. Die wichtigsten sind:

    public JDialog(Frame owner)
    public JDialog(Frame owner, boolean modal)
    public JDialog(Frame owner, String title)
    public JDialog(Frame owner, String title, boolean modal)
| javax.swing.JDialog

Als `owner` sollte der Aufrufer dabei das Fenster übergeben, zu dem der Dialog logisch gehört. Alle Konstruktoren gibt es auch in einer Form, bei der der `owner` vom Typ `Dialog` ist. Wahlweise kann ein `JDialog` auch ohne `owner` konstruiert werden (mit dem parameterlosen Konstruktor), doch dann kann es unter Umständen Fokusprobleme beim Wechsel zwischen mehreren Anwendungen geben.

Die übrigen Parameter geben den Titel des Dialogs an und legen fest, ob er modal oder nicht-modal sein soll. Bei einem modalen Dialog wird der Aufruf von `show` (bzw. `setVisible(true)`) erst dann beendet, wenn der Dialog geschlossen wurde. Bei einem nicht-modalen Dialog fährt das Programm dagegen unmittelbar mit der nächsten Anweisung hinter `show` fort.

### 36.1.4 JOptionPane

Eine weitere (und noch dazu sehr bequeme) Möglichkeit, Swing-Dialoge zu erzeugen, steht mit der Klasse `JOptionPane` zur Verfügung. Diese ist in der Lage, einfache Dialoge, die lediglich ein Icon und einen Text oder ein Eingabefeld und eine Auswahl der Buttons "Yes", "No" und "Cancel" enthalten, mit einem einzigen Aufruf einer statischen Methode zu erzeugen. `JOptionPane` ist sehr vielseitig, wir wollen uns an dieser Stelle allerdings auf ihre wichtigsten Anwendungen beschränken.

#### Anzeigen einer Nachricht

In seiner einfachsten Form kann `JOptionPane` dazu verwendet werden, ein Dialogfenster mit Titel, Icon und Hinweistext anzuzeigen, das mit Hilfe eines "OK"-Buttons beendet werden kann. Dazu stehen verschiedene Varianten der Methode `showMessageDialog` zur Verfügung:

    public static void showMessageDialog(
      Component parentComponent,
      Object message
    )

    public static void showMessageDialog(
      Component parentComponent,
      Object message,
      String title,
      int messageType
    )

    public static void showMessageDialog(
      Component parentComponent,
      Object message,
      String title,
      int messageType,
      Icon icon
    )
| javax.swing.JOptionPane

Aufrufe von `showMessageDialog` sind grundsätzlich modal. Erst wenn der Dialog vom Anwender beendet wurde, wird die nächste Anweisung ausgeführt. Der Parameter `parentComponent` bezeichnet die Vaterkomponente, aus der heraus der Dialog aufgerufen wurde, und `message` ist der anzuzeigende Text. Soll eine eigene Titelzeile angegeben werden, kann dazu `title` verwendet werden, andernfalls wird "Message" angezeigt. Wahlweise kann mit dem Parameter `icon` ein Icon angegeben werden, das neben dem Text angezeigt wird. Alternativ kann auch durch Übergabe einer der Konstanten `ERROR_MESSAGE`, `INFORMATION_MESSAGE`, `WARNING_MESSAGE`, `QUESTION_MESSAGE` oder `PLAIN_MESSAGE` an den Parameter `messageType` ein Standard-Icon erzeugt werden:

Abbildung 36.5: Die Standard-Icons bei JOptionPane

#### Eingabe einer Bestätigung

Mit den verschiedenen Varianten der Methode `showConfirmDialog` kann ein Dialog erzeugt werden, der neben den zuvor besprochenen Fähigkeiten die Möglichkeit bietet, die Auswahl der Buttons zu beeinflussen, mit denen der Dialog beendet werden kann.

    public static int showConfirmDialog(
      Component parentComponent,
      Object message
    )

    public static int showConfirmDialog(
      Component parentComponent,
      Object message,
      String title,
      int optionType
    )

    public static int showConfirmDialog(
      Component parentComponent,
      Object message,
      String title,
      int optionType,
      int messageType
    )

    public static int showConfirmDialog(
      Component parentComponent,
      Object message,
      String title,
      int optionType,
      int messageType,
      Icon icon
    )
| javax.swing.JOptionPane

Die Parameter entsprechen im wesentlichen denen der Methode `showMessageDialog`. Zusätzlich kann mit `optionType` eine der Konstanten `YES_NO_OPTION`, `OK_CANCEL_OPTION` oder `YES_NO_CANCEL_OPTION` angegeben werden, um zu entscheiden, welche Kombination von Buttons der Dialog anzeigen soll:

Abbildung 36.6: Button-Kombinationen bei showConfirmDialog

`showConfirmDialog` ist ebenfalls modal und ihr Rückgabewert zeigt an, auf welche Weise der Dialog beendet wurde:

Rückgabewert | Bedeutung
YES_OPTION | Mit dem "Yes"-Button
NO_OPTION | Mit dem "No"-Button
CANCEL_OPTION | Mit dem "Cancel"-Button
OK_OPTION | Mit dem "OK"-Button
CLOSED_OPTION | Mit dem "Schließen"-Button der Titelzeile

Tabelle 36.1: Rückgabewerte von showConfirmDialog

#### Eingabe von Daten

Mit der Möglichkeit, neben dem Icon und dem Nachrichtentext auch ein Textfeld im Dialog zu plazieren, kann `JOptionPane` auch zur Erfassung einfacher Daten verwendet werden. Die dazu aufzurufende Methode `showInputDialog` gibt es ebenfalls in mehreren Varianten:

    public static String showInputDialog(
      Component parentComponent,
      Object message
    )

    public static String showInputDialog(
      Component parentComponent,
      Object message,
      String title,
      int messageType
    )

    public static Object showInputDialog(
      Component parentComponent,
      Object message,
      String title,
      int messageType,
      Icon icon,
      Object[] selectionValues,
      Object initialSelectionValue
    )
| javax.swing.JOptionPane

In der einfachsten Form werden lediglich ein Dialog mit dem Titel "Input", die angegebene Nachricht mit einem Icon und das Textfeld zur Eingabe der Daten angezeigt. Die zweite Variante erlaubt zusätzlich die Angabe der Titelzeile und die Auswahl des Icontyps. In der letzten Variante wird anstelle des Textfelds eine Combobox zur Dateneingabe verwendet. Der Parameter `selectionValues` gibt die in der Combobox anzuzeigenden Elemente an (sie werden mit `toString` in Strings konvertiert), und `initialSelectionValue` gibt an, welches von ihnen standardmäßig selektiert werden soll.

Bei allen Methoden ist der Rückgabewert ein String mit den vom Anwender eingegebenen Daten. Falls eine Combobox zur Auswahl verwendet wurde, wird der String-Wert des ausgewählten Elements zurückgegeben. Ein Rückgabewert von `null` zeigt an, daß der Anwender den Dialog abgebrochen hat.

Das folgende Programm zeigt eine einfache Anwendung von `JOptionPane`, bei der der Anwender einen Wert aus einer vordefinierten Liste auswählen kann:

    001 /* Listing3604.java */
    002
    003 import javax.swing.*;
    004 import java.awt.*;
    005 import java.awt.event.*;
    006
    007 public class Listing3604
    008 extends JFrame
    009 {
    010   private static final String[] QUARTALE = {
    011     "1. Quartal", "2. Quartal", "3. Quartal", "4. Quartal"
    012   };
    013
    014   public Listing3604()
    015   {
    016     super("Test von JOptionPane");
    017     addWindowListener(new WindowClosingAdapter(true));
    018   }
    019
    020   public static void main(String[] args)
    021   {
    022     Listing3604 wnd = new Listing3604();
    023     wnd.setLocation(100, 100);
    024     wnd.setSize(300, 200);
    025     wnd.setVisible(true);
    026     String ret = (String)JOptionPane.showInputDialog(
    027       wnd,
    028       "Wählen Sie das Quartal aus",
    029       "JOptionPane.showInputDialog",
    030       JOptionPane.QUESTION_MESSAGE,
    031       null,
    032       QUARTALE,
    033       QUARTALE[2]
    034     );
    035     System.out.println("Ausgewählt wurde " + ret);
    036   }
    037 }
| Listing3604.java

Listing 36.4: Anwendungsbeispiel für JOptionPane

Der vom Programm erzeugte Dialog sieht unmittelbar nach dem Aufruf von `showInputDialog` so aus:

Abbildung 36.7: Die Methode showInputDialog

### 36.1.5 JApplet

Die Klasse `JApplet` ist eine einfache Erweiterung von `java.applet.Applet`. Sie dient zur Entwicklung von Applets, die Swing-Dialogelemente zur Gestaltung der Oberfläche verwenden. Die Unterschiede zwischen beiden Klassen sind nicht sehr umfangreich; insbesondere werden die Methoden `init`, `start`, `stop` und `destroy` in derselben Weise verwendet wie bei der Klasse `Applet`.

Wie bei den anderen Hauptfenstern ist auch hier der wichtigste Unterschied, daß `JApplet` die in Abschnitt 36.1.1 beschriebene RootPane-Struktur realisiert. Die Klasse implementiert ebenfalls das `RootPaneContainer`-Interface, und alle Komponenten müssen an die ContentPane übergeben werden. Da die Applet-Programmierung ab Kapitel 39 ausführlich beschrieben wird, wollen wir uns an dieser Stelle auf ein einfaches Beispiel beschränken:

    001 /* JAppletTest.java */
    002
    003 import javax.swing.*;
    004 import java.awt.*;
    005 import java.awt.event.*;
    006
    007 public class JAppletTest
    008 extends JApplet
    009 {
    010   public void init()
    011   {
    012     Container contentPane = getContentPane();
    013     contentPane.setLayout(new GridLayout(3, 1));
    014     contentPane.add(new JButton("Button 1"));
    015     contentPane.add(new JButton("Button 2"));
    016     contentPane.add(new JButton("Button 3"));
    017   }
    018 }
| JAppletTest.java

Listing 36.5: Anwendungsbeispiel für JApplet

Das Programm stellt ein sehr einfaches Applet dar, das - analog zu Listing 36.2 - drei Buttons in einem `GridLayout` anzeigt. Es kann wie folgt in eine HTML-Datei eingebettet werden:

    001 <html>
    002 <head><title>JAppletTest</title></head>
    003
    004 <body>
    005 <h1>JAppletTest</h1>
    006
    007 <applet code="JAppletTest.class" width=300 height=200>
    008 Hier steht das JAppletTest
    009 </applet>
    010
    011 </body>
    012 </html>
| japplet.html

Listing 36.6: HTML-Datei für JApplet-Beispiel

Das Applet kann nun mit Hilfe des AppletViewers gestartet werden:

    appletviewer japplet.html

### 36.1.6 JInternalFrame

Bei vielen Programmen ist es üblich, daß sie ein einziges Hauptfenster besitzen und ihre zahlreichen, gleichzeitig geöffneten Kindfenster innerhalb dieses Hauptfensters anordnen. Diese unter Windows als MDI (Multiple Document Interface) bezeichnete Technik ist bei bestimmten Typen von Anwendungen mittlerweile weitverbreitet (z.B. bei Textverarbeitungen, Grafikprogrammen oder Entwicklungsumgebungen).

Während im AWT keine Möglichkeit vorgesehen war, MDI-Anwendungen zu entwickeln, ist es in Swing recht einfach. Dazu werden lediglich zwei Arten von Komponenten benötigt:

- Das als Desktop bezeichnete Hauptfenster der Anwendung.
- Die Kindfenster der Anwendung.

#### Der Desktop

Als Desktop ist prinzipiell jede beliebige Hauptfensterklasse geeignet, meist wird aber die Klasse `JFrame` verwendet. Um die Kindfenster zu verwalten, wird die vordefinierte ContentPane durch eine Instanz der Klasse `JDesktopPane` ersetzt. Diese von `JLayeredPane` abgeleitete Klasse besitzt einen `DesktopManager`, der für die Verwaltung der Kindfenster zuständig ist. Der `DesktopManager` wird beispielsweise benachrichtigt (und führt alle dazu erforderlichen Aktionen aus), wenn ein Kindfenster verkleinert, vergrößert oder verschoben werden soll.

|  | `DesktopManager` ist ein Interface, das eine Vielzahl von Methoden enthält. Mit der Klasse `DefaultDesktopManager` gibt es eine Standardimplementierung, die für viele Zwecke ausreichend ist. |  |
Hinweis

#### Die Kindfenster

Die Kindfenster werden aus `JInternalFrame` abgeleitet; einem Dialogelement, das recht überzeugend vortäuscht, ein Hauptfenster zu sein. Tatsächlich ist `JInternalFrame` direkt aus `JComponent` abgeleitet und nicht aus `JFrame`, wie man es vielleicht vermuten könnte. Der Grund liegt darin, daß `JFrame` als `Frame` stets eine betriebssystemspezifische Fensterressource besitzt und damit für die Verwendung innerhalb der Grenzen eines anderen Fensters ungeeignet ist.

Eine betriebssystemspezifische Ressource für MDI-Kindfenster steht aber nicht auf allen grafischen Oberflächen zur Verfügung, und so haben sich die Swing-Entwickler entschlossen, `JInternalFrame` als leichtgewichtige Komponente selbst zu realisieren.

Das hat einige Konsequenzen:

- Die üblichen Ausstattungsmerkmale und Bedienelemente eines Fensters müssen nachgebildet werden (Rahmen, Titelleiste, Systemmenü, Buttons, das Verschieben des Fensters und das Verändern seiner Größe usw.).
- Die Programmierschnittstelle der Klasse `Frame` muß nachgebildet werden.
- Da `JInternalFrame` nicht aus `Frame` abgeleitet ist, kann ein `JInternalFrame`-Objekt nicht verwendet werden, wenn eine Variable oder ein Parameter vom Typ `Frame` gefordert ist.
- Muß eine Anwendung sowohl in einem `Frame` als auch in einem `JInternalFrame` laufen können, so muß der Code für die Implementierung des Fensters in beiden Klassen verfügbar gemacht werden. Vererbung kommt wegen der unterschiedlichen Vererbungslinien beider Klassen nicht in Frage. Als Lösung für dieses Problem könnte das Delegate-Pattern aus Abschnitt 10.3.6 angewendet werden.

|  |  |  | Um die Kindfenster zu aktivieren, müssen sie durch Aufruf von `setVisible` sichtbar gemacht und mit `add` auf dem Desktop plaziert werden. Bis zum JDK 1.2 war der Aufruf von `setVisible` nicht unbedingt erforderlich, denn ein `JInternalFrame` war standardmäßig sichtbar. Zur Angleichung an die anderen Hauptfensterklassen wurde dies mit dem JDK 1.3 geändert. |  |
JDK1.1-1.4

`JInternalFrame` besitzt diverse Konstruktoren. Das nachfolgende Syntaxdiagramm zeigt nur den umfangreichsten von ihnen. Alle anderen ergeben sich, indem man auf der rechten Seite nicht benötigte Argumente wegfallen läßt:

    public JInternalFrame(
      String title,
      boolean resizable,
      boolean closable,
      boolean maximizable,
      boolean iconifiable
    )
| javax.Swing.JInternalFrame

`JInternalFrame` implementiert das schon erläuterte Interface `RootPaneContainer` und stellt zusätzlich verschiedene Methoden zur Verfügung. Einige von ihnen sind:

    public void setClosable(boolean b)
    public void setResizable(boolean b)
    public void setIconifiable(boolean b)
    public void setMaximizable(boolean b)

    public void setTitle(String title)

    public void setDefaultCloseOperation(int operation)
| javax.Swing.JInternalFrame

`setClosable` entscheidet, ob das Fenster geschlossen, und `setResizable`, ob seine Größe verändert werden kann. `setIconifiable` legt fest, ob das Fenster in ein Symbol verkleinert, und `setMaximizable`, ob es maximiert werden kann. Mit `setTitle` kann der Rahmentitel angegeben werden, und mit `setDefaultCloseOperation` wird angegeben, wie das Fenster sich beim Schließen verhalten soll. Hier kann eine der Konstanten `DO_NOTHING_ON_CLOSE`, `HIDE_ON_CLOSE` oder `DISPOSE_ON_CLOSE` angegeben werden.

#### Beispielprogramm

Nach diesen Vorbemerkungen wollen wir ein einfaches Beispielprogramm erstellen. Es soll einen `JFrame` als Desktop mit zwei Kindfenstern enthalten. Diese sollen alle Standardbedienelemente enthalten, vergrößer- und verkleinerbar sein und innerhalb des Desktops verschoben werden können.

Wir erstellen dazu eine Klasse `DesktopFrame`, die mit einem `DefaultDesktopManager` ein Desktop aufbaut. Mit ihrer Methode `addChild` kann ein Kindfenster an einer bestimmten Position auf dem Desktop plaziert werden. Weiterhin definieren wir eine aus `JInternalFrame` abgeleitete Klasse `ChildFrame`. Sie legt im Konstruktor die Eigenschaften des Fensters fest, hat darüber hinaus aber keine Funktionalitäten.

    001 /* Listing3607.java */
    002
    003 import java.awt.*;
    004 import java.awt.event.*;
    005 import javax.swing.*;
    006
    007 class DesktopFrame
    008 extends JFrame
    009 {
    010   private JDesktopPane desk;
    011
    012   public DesktopFrame()
    013   {
    014     super("DesktopFrame");
    015     this.desk = new JDesktopPane();
    016     desk.setDesktopManager(new DefaultDesktopManager());
    017     setContentPane(desk);
    018     addWindowListener(new WindowClosingAdapter(true));
    019   }
    020
    021   public void addChild(JInternalFrame child, int x, int y)
    022   {
    023     child.setLocation(x, y);
    024     child.setSize(200, 150);
    025     child.setDefaultCloseOperation(
    026       JInternalFrame.DISPOSE_ON_CLOSE
    027     );
    028     desk.add(child);
    029     child.setVisible(true);
    030   }
    031 }
    032
    033 class ChildFrame
    034 extends JInternalFrame
    035 {
    036   public ChildFrame(String title)
    037   {
    038     super("Child " + title, true, true);
    039     setIconifiable(true);
    040     setMaximizable(true);
    041     setBackground(Color.lightGray);
    042   }
    043 }
    044
    045 public class Listing3607
    046 {
    047   public static void main(String[] args)
    048   {
    049     //Desktop erzeugen
    050     DesktopFrame desktop = new DesktopFrame();
    051     desktop.setLocation(100, 100);
    052     desktop.setSize(400, 300);
    053     desktop.setVisible(true);
    054     //Zwei ChildFrames hinzufügen
    055     desktop.addChild(new ChildFrame("1"), 10, 10);
    056     desktop.addChild(new ChildFrame("2"), 20, 20);
    057   }
    058 }
| Listing3607.java

Listing 36.7: Die Klasse JInternalFrame

Die Ausgabe des Programms sieht so aus:

Abbildung 36.8: Die Klasse JInternalFrame

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de