Im AWT des JDK 1.0.x gab es einen schwerwiegenden Fehler, durch den das Erzeugen modaler Dialoge unmöglich gemacht wurde. Dieser Fehler hielt sich bis zur Version 1.0.2 und wurde erst mit Erscheinen des JDK 1.1 behoben. Da viele Anwenderdialoge von Natur aus modalen Charakter haben, wurde dieser Fehler als recht schwerwiegend angesehen, und die Java-Gemeinde entwickelte eine Reihe von Workarounds, die aber allesamt nicht voll zufriedenstellen konnten. Glücklicherweise sind diese Probleme im JDK 1.1 vergessen, und wir wollen in diesem Kapitel aufzeigen, wie modale Dialoge in Java erzeugt werden können.
Ein modaler Dialog muß immer aus der Klasse Dialog abgeleitet werden. Nur sie bietet die Möglichkeit, an den Konstruktor einen booleschen Wert zu übergeben, der festlegt, daß die übrigen Fenster der Anwendung während der Anzeige des Dialogs suspendiert werden. Dialog besitzt insgesamt vier Konstruktoren:
public Dialog(Frame parent); public Dialog(Frame parent, boolean modal); public Dialog(Frame parent, String title); public Dialog(Frame parent, String title, boolean modal);
Zunächst muß das Frame-Objekt übergeben werden, aus dem heraus der Dialog aufgerufen wird. Mit title kann der Inhalt der Titelzeile vorgegeben werden, und der Parameter modal entscheidet, ob der Dialog modal wird oder nicht. Der Aufruf eines Dialogs aus einem anderen Dialog ist damit eigentlich nicht möglich, denn parent muß ein Frame sein. Durch Aufruf von getParent (aus der Klasse Component) kann zu einem Dialog allerdings der Vaterframe bestimmt und zur Instanzierung eines weiteren Dialogs verwendet werden:
public Component getParent();
Dialog bietet die Methoden isModal und setModal, mit denen auf die Eigenschaft des Dialogs, modal oder nicht-modal zu sein, zugegriffen werden kann:
public boolean isModal(); public void setModal(boolean b);
Der Rückgabewert von isModal ist true, falls der Dialog modal ist. Andernfalls ist er false.
Die Methode setModal, deren Fähigkeit darin besteht, einen Dialog zwischen den Zuständen modal und nicht-modal umzuschalten zu können, ist mit Vorsicht zu genießen. Unter Windows 95 hat ein Umschalten von modal auf nicht-modal, wenn es nach dem Anzeigen des Fensters ausgeführt wurde, mitunter dazu geführt, daß beim Schließen des Fensters die Nachrichtenschleife des Aufrufers deaktiviert blieb und das Programm sich aufhängte.
Ein zusätzliches Feature der Klasse Dialog besteht darin, die Veränderbarkeit der Größe eines Fensters durch den Anwender zu unterbinden:
public void setResizable(boolean resizable); public boolean isResizable();
Nach einem Aufruf von setResizable mit false als Argument kann die Größe des Fensters nicht mehr vom Anwender verändert werden. Nach einem Aufruf von setResizable(true) ist dies wieder möglich. Mit isResizable kann der aktuelle Zustand dieses Schalters abgefragt werden.
Das folgende Beispiel demonstriert die Anwendung der Klasse Dialog. Es zeigt einen Frame, aus dem per Buttonklick ein modaler Dialog aufgerufen werden kann. Der Dialog fragt den Anwender, ob die Anwendung beendet werden soll und wartet, bis einer der Buttons "Ja" oder "Nein" gedrückt wurde. In diesem Fall wird ein boolescher Rückgabewert generiert, der nach dem Schließen des Dialogs an das Vaterfenster zurückgegeben wird. Falls der Rückgabewert true (entprechend dem Button "Ja") war, wird die Anwendung beendet, andernfalls läuft sie weiter:
import java.awt.*;
import java.awt.event.*;
class YesNoDialog
extends Dialog
implements ActionListener
{
boolean result;
public YesNoDialog(Frame parent, String msg)
{
super(parent, "Ja-/Nein-Auswahl", true);
//Fenster
setBackground(Color.lightGray);
setLayout(new BorderLayout());
setResizable(false);
Point parloc = parent.getLocation();
setLocation(parloc.x + 30, parloc.y + 30);
//Message
add("Center", new Label(msg));
//Buttons
Panel panel = new Panel();
panel.setLayout(new FlowLayout(FlowLayout.CENTER));
Button button = new Button("Ja");
button.addActionListener(this);
panel.add(button);
button = new Button("Nein");
button.addActionListener(this);
panel.add(button);
add("South", panel);
pack();
}
public void actionPerformed(ActionEvent event)
{
result = event.getActionCommand().equals("Ja");
setVisible(false);
dispose();
}
public boolean getResult()
{
return result;
}
}
public class Example2108
extends Frame
implements ActionListener
{
public static void main(String[] args)
{
Example2108 wnd = new Example2108();
wnd.setVisible(true);
}
public Example2108()
{
super("Example2108");
setLayout(new FlowLayout());
setBackground(Color.lightGray);
Button button = new Button("Ende");
button.addActionListener(this);
add(button);
setLocation(100,100);
setSize(300,200);
setVisible(true);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("Ende")) {
YesNoDialog dlg;
dlg = new YesNoDialog(
this,
"Wollen Sie das Programm wirklich beenden?"
);
dlg.setVisible(true);
//Auf das Schließen des Dialogs warten...
if (dlg.getResult()) {
setVisible(false);
dispose();
System.exit(0);
}
}
}
}
|
|
Um den Dialog relativ zur Position seines Vaterfensters anzuzeigen, wird dessen Position durch Aufruf von getLocation ermittelt. Der Ursprung des Dialogfensters wird dann 30 Pixel weiter nach unten bzw. nach rechts gelegt. |
Nach dem Start zeigt das Programm zunächst das in Abbildung 21.11 dargestellte Fenster an:
Abbildung 21.11: Das Vaterfenster für den modalen Dialog
Nach dem Klick auf "Ende" wird actionPerformed aufgerufen und läuft bis zum Aufruf des Dialogs (siehe Abbildung 21.12). Die Anweisung dlg.setVisible(true); terminiert erst dann, wenn der Dialog durch Drücken eines Buttons beendet wurde. In diesem Fall wird mit getResult der Rückgabewert abgefragt und das Programm beendet, wenn dieser true ist. Die Methode getResult liefert den Inhalt der privaten Instanzvariablen result, die beim Auftreten eines Action-Events durch einen der beiden Buttons gesetzt wird. Zwar ist das Dialogfenster beim Aufruf von getResult bereits zerstört, aber die Objektvariable dlg existiert noch und getResult kann aufgerufen werden.
Abb. 21.12: Ein einfacher Ja-/Nein-Dialog
Bevor wir das Kapitel beenden, wollen wir dieses Programm ein wenig erweitern und ein weiteres Beispiel für die Anwendung modaler Dialoge geben. Das folgende Programm implementiert eine Klasse ModalDialog, die aus Dialog abgeleitet ist. Mit ihrer Hilfe können modale Dialoge der obigen Art konstruiert werden, bei denen die Ausstattung mit Buttons variabel ist und zum Zeitpunkt der Instanzierung festgelegt werden kann:
public ModalDialog( Frame parent, String title, String msg, String buttons );
Der Konstruktor erwartet neben dem Vater-Frame drei weitere Parameter, title, msg und buttons. In title wird der Inhalt der Titelzeile übergeben, und msg spezifiziert den Text innerhalb des Dialogs. Der Parameter buttons erwartet eine Liste von Button-Bezeichnern, die zur Festlegung der Anzahl und Beschriftung der Buttons dienen. Die einzelnen Elemente sind durch Kommata zu trennen und werden innerhalb des Dialogs mit einem StringTokenizer zerlegt. Der in getResult gelieferte Rückgabewert des Dialogs ist - anders als im vorigen Beispiel - ein String und entspricht der Beschriftung des zum Schließen verwendeten Buttons.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class ModalDialog
extends Dialog
implements ActionListener
{
String result;
public static String OKDialog(Frame parent, String msg)
{
ModalDialog dlg;
dlg = new ModalDialog(parent,"Nachricht",msg,"OK");
dlg.setVisible(true);
return dlg.getResult();
}
public static String YesNoDialog(Frame parent, String msg)
{
ModalDialog dlg;
dlg = new ModalDialog(parent,"Frage",msg,"Ja,Nein");
dlg.setVisible(true);
return dlg.getResult();
}
public static String YesNoCancelDialog(Frame parent, String msg)
{
ModalDialog dlg;
dlg = new ModalDialog(parent,"Frage",msg,"Ja,Nein,Abbruch");
dlg.setVisible(true);
return dlg.getResult();
}
public ModalDialog(
Frame parent,
String title,
String msg,
String buttons
)
{
super(parent, title, true);
//Fenster
setBackground(Color.lightGray);
setLayout(new BorderLayout());
setResizable(false);
Point parloc = parent.getLocation();
setLocation(parloc.x + 30, parloc.y + 30);
//Message
add("Center", new Label(msg));
//Buttons
Panel panel = new Panel();
panel.setLayout(new FlowLayout(FlowLayout.CENTER));
StringTokenizer strtok = new StringTokenizer(buttons,",");
while (strtok.hasMoreTokens()) {
Button button = new Button(strtok.nextToken());
button.addActionListener(this);
panel.add(button);
}
add("South", panel);
pack();
}
public void actionPerformed(ActionEvent event)
{
result = event.getActionCommand();
setVisible(false);
dispose();
}
public String getResult()
{
return result;
}
}
public class Example2109
extends Frame
implements ActionListener
{
public static void main(String[] args)
{
Example2109 wnd = new Example2109();
wnd.setVisible(true);
}
public Example2109()
{
super("Example2109");
setLayout(new FlowLayout());
setBackground(Color.lightGray);
Button button = new Button("OKDialog");
button.addActionListener(this);
add(button);
button = new Button("YesNoDialog");
button.addActionListener(this);
add(button);
button = new Button("YesNoCancelDialog");
button.addActionListener(this);
add(button);
setLocation(100,100);
setSize(400,200);
setVisible(true);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("OKDialog")) {
ModalDialog.OKDialog(this,"Dream, dream, dream, ...");
} else if (cmd.equals("YesNoDialog")) {
String ret = ModalDialog.YesNoDialog(
this,
"Programm beenden?"
);
if (ret.equals("Ja")) {
setVisible(false);
dispose();
System.exit(0);
}
} else if (cmd.equals("YesNoCancelDialog")) {
String msg = "Verzeichnis erstellen?";
String ret = ModalDialog.YesNoCancelDialog(this,msg);
ModalDialog.OKDialog(this,"Rückgabe: " + ret);
}
}
}
Die drei Dialoge, die durch Aufrufe von OKDialog, YesNoDialog und YesNoCancelDialog generiert werden, sind in den folgenden Abbildungen 21.13 bis 21.15 dargestellt:
Abbildung 21.13: Ein Aufruf von OKDialog
Abbildung 21.14: Ein Aufruf von YesNoDialog
Abbildung 21.15: Ein Aufruf von YesNoCancelDialog