Inhalt |
|
|
|
|
|
|
|
|
|
|
|
|
Wir haben schon im Kapitel über Grafikausgaben und im Kapitel über Events eine Reihe von Komponenten (Panel, Canvas, Button) kennen gelernt, die sich in Java-Applikationen mit graphischer Benutzeroberfläche einsetzen lassen. Dort hatten wir auch schon Layouts kennen gelernt zur Anordnung dieser Komponenten.
Wir wollen hier zunächst die wichtigsten Komponenten kennen lernen, mit denen sich Text ein- und ausgeben lässt.
Die Komponente zum Anzeigen eines statischen Textes heißt Label. Der Text lässt sich entweder mit dem Konstruktor oder mit der Methode setText setzen. Ein Label ist immer einzeilig.
Die Komponente, mit der der Benutzer einzeiligen Text ein- und ausgeben kann, ist JTextField. Die Textvorgabe erfolgt genau wie bei Label. Man kann das JTextField nicht-editierbar machen, um nur einen Text anzuzeigen. Gewöhnlich will man aber, dass der Benutzer den angezeigten Text verändern kann. Dann kann man den Text einfach mit getText auslesen. Ein JTextfield kann auch ein ActionEvent produzieren, wenn der Benutzer die Enter-Taste drückt.
Will man mehrzeiligen Text ein- und ausgeben, so verwendet man eine JTextArea. Im Unterschied zum TextField erzeugt diese Methode kein Event, wenn die Enter-Taste gedrückt wird, sondern erzeugt dadurch einen neuen Paragraphen. Man verwendet gewöhnlich kein Event dieser Komponenten, sondern wertet den Inhalt z.B. beim Druck auf einen Knopf aus.
Damit wir im Text scrollen können, verwenden wir eine JScrollPane. Dies ist eine Komponente, die ein JTextField aufnehmen kann und Scrollbalken anzeigt, sofern dies notwendig ist.
import java.awt.*; import javax.swing.*; import java.awt.event.*; class TestFrame extends JFrame implements ActionListener { JTextArea Out; JTextField In; public TestFrame () { super("Test"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); setSize(500,400); // Label: add("North",new JLabel("Test",JLabel.CENTER)); // TextArea: Out=new JTextArea(); JScrollPane OutP=new JScrollPane(Out); add("Center",OutP); OutP.setPreferredSize(new Dimension(400,400)); // TextField: add("South",In=new JTextField()); In.addActionListener(this); pack(); setVisible(true); } public void actionPerformed (ActionEvent e) { if (e.getSource()==In) // enter in TextField { Out.append(In.getText()+"\n"); In.setText(""); } } } public class Test { public static void main (String args[]) { new TestFrame(); } }
Das Beispiel funktioniert so, dass der Benutzer in der Eingabezeile unten einen Text eingeben kann. Drückt man dort die Enter-Taste, so wird der Text an die JTextArea oben angehängt und die Eingabezeile gelöscht.
Es wäre in diesem Beispiel sinnvoll, die JTextArea durch setEditable(false) nicht edierbar zu machen. Außerdem könnte man mit setBackground(Color.gray) den Hintergrund z.B. auf Grau einstellen.
Wir besprechen hier zwei Layout-Varianten, die vom Benutzer verändert werden können.
JSplitPlane
erzeugt eine vertikal oder
horizontal geteilte Ansicht, wobei der Benutzer die Teilung aktiv verschieben
kann. Der Container enthält also zwei Komponenten. Deren Minimalgröße bestimmt,
wie weit sich der Schieber nach rechts oder links verschieben lässt. Wir setzen
daher die Minimalgröße unserer Canvas
auf 10 mal 10 Pixel. Zu
beachten ist noch, dass der übrige Raum zwischen den Komponenten verteilt wird.
Wir wollen eine gleiche Aufteilung und setzen daher das Aufteilungsgewicht auf
0.5.
import java.awt.*; import javax.swing.*; /** * Eine Canvas, die mit Farbe gefüllt wird. */ class TestCanvas extends Canvas { Color C; public TestCanvas (Color c) { C=c; setMinimumSize(new Dimension(10,10)); } public void paint (Graphics g) { Dimension d=getSize(); g.setColor(C); g.fillRect(0,0,d.width,d.height); } } /** * Das ist ein Panel mit zwei horizontalen Farbfeldern. * Der Benutzer kann die Trennung verschieben. */ class TestPanel extends JPanel { public TestPanel () { setPreferredSize(new Dimension(400,200)); // Erzeuge eine SplitPane mit zwei Farbfeldern: JSplitPane pane=new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, new TestCanvas(Color.RED), new TestCanvas(Color.GREEN)); pane.setResizeWeight(0.5); // In der Mitte hinzufügen: setLayout(new BorderLayout()); add("Center",pane); } } public class Test extends JFrame { public Test () { super("Test"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add("Center",new TestPanel()); pack(); } public static void main (String args[]) { JFrame F=new Test(); F.setVisible(true); } }
Die andere Art, mehrere Komponenten auf kleinem Raum
unterzubringen ist JTabbedPane
.
Dazu müssen wir nur den Code folgendermaßen ändern.
JTabbedPane pane=new JTabbedPane(); pane.add("Rot",new TestCanvas(Color.RED)); pane.add("Grün",new TestCanvas(Color.GREEN));
Es gibt noch die Möglichkeit, Icons anstelle von Text und Tooltips (Texte, die beim Verharren der Maus über dem Reiter erscheinen) zu verwenden.
Oftmals will man den Benutzer aus mehreren Möglichkeiten
auswählen lassen. Dazu gibt es die JComboBox
. Beim Klick auf ein
Klappfeld öffnet sich eine Liste mit Möglichkeiten. Wählt der Benutzer eine
davon aus, so wird ein ActionEvent
ausgelöst. Im folgenden Programm
fangen wir dieses Event ab und stellen das Resultat in einen Label dar.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Test extends JFrame implements ActionListener { JLabel label; JComboBox box; String strings[]={"Apfel","Birne","Kirsche"}; public Test () { super("Test"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); label=new JLabel("",JLabel.CENTER); label.setPreferredSize(new Dimension(100,50)); box=new JComboBox(strings); box.addActionListener(this); setLayout(new BorderLayout()); add("Center",label); add("South",box); pack(); } public void actionPerformed (ActionEvent e) { if (e.getSource()==box) { if (box.getSelectedIndex()>=-1) { label.setText(box.getSelectedItem().toString()); } } } public static void main (String args[]) { JFrame F=new Test(); F.setVisible(true); } }
Man kenn den ausgewählten Index abfragen oder auch das
ausgewählte Objekt, das man mit toString
umwandeln kann. Natürlich
kann man die Auswahl auch über das Programm setzen. Näheres findet man in der
Dokumentation.
Die entsprechende Komponente des AWT heißt Choice
und arbeitet ziemlich ähnlich.
Will man alle Auswahlmöglichkeiten gleichzeitig darstellen,
so gibt es JList
. Diese neue Komponente erzeugt ein Ereignis vom
Typ ListSelectionEvent
, das es nur in Swing gibt. Es muss daher ein
anderes Paket eingebunden werden. Im folgenden Programm betten wir die Liste
außerdem mit Scrollbalken ein, die erscheinen würden, wenn die Liste zu lang
wäre.
import java.awt.*; import javax.swing.*; import javax.swing.event.*; public class Test extends JFrame implements ListSelectionListener { JLabel label; JList list; String strings[]={"Apfel","Birne","Kirsche"}; public Test () { super("Test"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); label=new JLabel("",JLabel.CENTER); label.setPreferredSize(new Dimension(100,50)); list=new JList(strings); list.addListSelectionListener(this); setLayout(new BorderLayout()); add("Center",label); add("South",new JScrollPane(list)); pack(); } public void valueChanged (ListSelectionEvent e) { if (e.getSource()==list) { if (list.getSelectedIndex()>=-1) { label.setText(list.getSelectedValue().toString()); } } } public static void main (String args[]) { JFrame F=new Test(); F.setVisible(true); } }
Gemeint sind die Auswahlmenüs, die unterhalb der Titelzeile von Fenstern erscheinen. Es gibt daneben in Java 1.1 auch noch Popup-Menüs, die mitten auf einer Seite erscheinen können.
Ein Menü besteht aus einem MenuBar, der verschiedene Menu-Objekte enthält. Jedes ist ein Klappmenü, das wiederum MenuItem-Objekte enthält, die der Benutzer letztendlich wählen kann. Das Menü wird im Konstruktor des Frames aufgebaut. Das entsprechende Code-Segment sieht so aus:
// Erzeuge Menueleiste MenuBar menu=new MenuBar() setMenuBar(menu); // Ezeuge ein Menue Menu files=new Menu("Files"); menu.add(files); // Erzeuge darin einen Menue-Eintrag MenuItem ItemClose=new MenuItem("Close"); ItemClose.setActionListener(this); files.add(ItemClose);
Wie man sieht, ist der Prozess dreistufig: Menüleiste, Menü und Menüeintrag. Man kann allerdings auch Menüs in Menüs schachteln. Dies erzeugt Aufklapp-Menüs, wie man sie von graphischen Benutzoberflächen her kennt.
Es gibt neben normalen MenuItem auch CheckboxItem, die ein Häkchen erhalten, wenn sie angewählt sind.
MenuItem-Objekte erzeugen Action-Events wie Button-Objekte. CheckboxItem-Objekte erzeugen dagegen Item-Events, die von einem ItemListener mit der Methode itemStateChanged abgehört werden können.
Popup-Menüs werden gewöhnlich durch einen rechten Mausklick auf das Fenster ausgelöst. Das folgende Programm demonstriert die Erzeugung eines Menüs und eines Popup-Menüs. Die Ereignisse des verwendeten CheckboxMenuItem "Ignore" werden hier nicht abgefangen. Stattdessen wird im Bedarfsfall dessen Status abgefragt.
import java.awt.*; import java.awt.event.*; class TestFrame extends Frame implements ActionListener { CheckboxMenuItem Ignore; PopupMenu PM; public TestFrame () { // build up the menu bar MenuBar m=new MenuBar(); setMenuBar(m); Menu test=new Menu("Test"); // first menu m.add(test); MenuItem item=new MenuItem("Close"); // an item item.addActionListener(this); test.add(item); test.add(Ignore=new CheckboxMenuItem("Ignore")); // a checkbox item Ignore.setState(true); // popup menu: PM=new PopupMenu(); MenuItem mi=new MenuItem("Close"); mi.addActionListener(this); PM.add(mi); add(PM); addMouseListener( // for the popup menu new MouseAdapter () { public void mousePressed (MouseEvent e) { if (e.isPopupTrigger() || e.isMetaDown()) // right mouse click { PM.show(e.getComponent(), e.getX(),e.getY()); } } } ); setSize(400,400); show(); } public void actionPerformed (ActionEvent e) { if (e.getActionCommand().equals("Close") && !Ignore.getState()) { dispose(); System.exit(0); } } } public class Test { public static void main (String args[]) { new TestFrame(); } }
Das Popup-Menu wird über einen MouseAdapter aktiviert. Dazu wird auf Druck der rechten Maustaste reagiert. Da es auf manchen Systemen nur eine Maustaste gibt, ist dies in Java dadurch simuliert, dass es wie der Druck der Meta-Taste gleichzeitig mit der Maustaste weitergegeben wird. Daneben gibt es noch eine Abfrage darauf, ob ein Popup-Trigger ausgelöst wurde, die wir ebenfalls verwenden, obwohl sie unter Windows nicht aktiv ist.
Das Popup-Menü erzeugt nach seiner Anzeige Events, wenn der Benuter einen Menüpunkt auswählt. Es informiert dann die registrierten Listener über diese Events, genau wie das normale Menü.
Dialoge sind kleine Fenster, die zu einen Frame gehören, und für Benutzereingaben eingeblendet werden. Beispiele sind Ja/Nein-Abfragen, Suchen und Ersetzen oder auch der Dialog zur Dateiauswahl.
Dialoge können modal oder nicht-modal sein.
Die Erzeugung eines Dialogs ist ähnlich wie die Erzeugung eines Fensters. Man erzeugt einer Instanz der Dialog-Klasse mit new TestDialog(), wobei TestDialog ein Kind von Dialog ist, das im Konstruktor den Dialog aufbaut. Dann muss der Dialog noch mit show (oder besser setVisible(true)). angezeigt werden. Bei nicht-modalen Dialogen läuft das Programm danach normal weiter.
Als Beispiel erzeugen wir einen Dialog, der abfragt, ob das Fenster wirklich geschlossen werden soll. Wir machen den Dialog natürlich modal.
class CloseQuestion extends Dialog implements ActionListener { private boolean Yes=false; public CloseQuestion (Frame f) { super(f,"Close",true); // call constructor of dialog with modal-flag true // Label for the question: add("North",new Label("Really close?")); // Panel for the yes/no buttons Panel p=new Panel(); Button b; p.add(b=new Button("Yes")); b.addActionListener(this); p.add(b=new Button("No")); b.addActionListener(this); add("South",p); // pack set it 100x100 off the frame and show it // (blocking the frame) pack(); setLocation(f.getLocation().x+100,f.getLocation().y+100); show(); } public void actionPerformed (ActionEvent e) { if (e.getActionCommand().equals("Yes")) // pressed the yes button { Yes=true; } dispose(); // frame continues its job } public boolean isYes () // to ask for the result { return Yes; } }
Interessant ist, wie die Größe eines Dialogs eingestellt werden kann. Die Methode pack stellt hier eine Minimalgröße ein, die genügt, um alle Dialogelement anzuzeigen. Um dabei Textfeldern eine genügende Größe zu geben gibt es Konstruktoren von TextField und TextArea, die eine gewisse Spalten- und Zeilenzahl fordern. Bei anderen Elemenenten (z.B. Canvas) kann man getPreferredSize überschreiben, um die gewünschte Größe sicherzustellen.
Als komplexeres Beispiel für einen nicht-modalen Dialog erzeugen wir einen Dialog, der die Auswahl der aktiven Farbe erlaubt. Das Beispiel zeigt außerdem die Behandlung von Scroll-Balken.
Weiterhin zeigt das Programm, wie Objekte miteinander kommunizieren können. Der EventDialog benachrichtigt nämlich einen ColorEditListener über die gerade gewählte Farbe und darüber, wann der Dialog beendet wurde. In unserem Fall fungiert das Fenster selbst als ColorEditListener. Wir übergeben den Listener der Einfachheit halber im Konstruktor des Dialogs. Man sollte wohl eigentlich eine addColorEditListener-Methode schreiben und mehrere Listener vorsehen. Dies bleibt einer Übungsaufgabe vorbehalten.
Da dieses Beispiel recht komplex ist, beschreiben noch einmal die wichtigsten Komponenten
import java.awt.*; import java.awt.event.*; interface ColorEditListener // Interface, das vom Dialog benachrichtigt wird { public void setColor (Color c); public void close (); } class ColorScrollbar extends Panel // ein einzelner Scrollbar (fuer jede Farbe einen) implements AdjustmentListener // fuer Aenderungen im Scrollbar { public int Value; ColorEdit CE; Scrollbar SB; public ColorScrollbar (ColorEdit ce, String s, int value) { CE=ce; Value=value; setLayout(new GridLayout(1,0)); add(new Label(s)); // links das Label add(SB=new Scrollbar(Scrollbar.HORIZONTAL,value,40,0,255)); // daneben der Scrollbar, // horizontal mit Werten von 0 bis 255, // 40 ist die Breite des Schiebers. SB.addAdjustmentListener(this); } public void adjustmentValueChanged (AdjustmentEvent e) { Value=SB.getValue(); // lies neuen Wert SB.setValue(Value); // Scrollbar dort fixieren CE.setcolor(); } public Dimension getPreferredSize () // Damit der Scrollbar nicht zu knapp ist { return new Dimension(200,20); } public int value () { return Value; } } class ColorEdit extends Dialog // der Farb-Editor implements ActionListener { ColorScrollbar Red, Green, Blue; Label RedLabel,GreenLabel,BlueLabel; Color C,Cold; Panel CP; String Name; ColorEditListener CL; public ColorEdit (Frame F, ColorEditListener cl, Color c) { super(F,"Edit Color",false); CL=cl; Cold=C=c; // Panel fuer die Scrollbars: Panel p=new Panel(); p.setLayout(new GridLayout(0,1)); p.add(Red=new ColorScrollbar(this,"Red",C.getRed())); p.add(Green=new ColorScrollbar(this,"Green",C.getGreen())); p.add(Blue=new ColorScrollbar(this,"Blue",C.getBlue())); add("Center",p); // Panel fuer die Buttons: Panel pb=new Panel(); Button b; pb.add(b=new Button("OK")); b.addActionListener(this); pb.add(b=new Button("Cancel")); b.addActionListener(this); add("South",pb); // Panel fuer das Farb-Display CP=new Panel(); CP.add(new Label("Your Color")); CP.setBackground(C); add("North",CP); pack(); show(); } public void actionPerformed (ActionEvent e) { if (e.getActionCommand().equals("Cancel")) { C=Cold; tell(); // alte Farbe zuruecksetzen } CL.close(); dispose(); } public void setcolor () // benachrichtigt den Listener { C=new Color(Red.value(),Green.value(),Blue.value()); CP.setBackground(C); CP.repaint(); // Zeichnet das Farb-Display neu tell(); } void tell () // Benachrichtgung an Listener { CL.setColor(C); } } class TestFrame extends Frame implements ColorEditListener { ColorEdit CE=null; Color C; public TestFrame () { addWindowListener( new WindowAdapter () { public void windowClosing (WindowEvent e) { dispose(); System.exit(0); } } ); addMouseListener( new MouseAdapter () { public void mousePressed (MouseEvent e) { coloredit(); } } ); C=Color.gray; // sowas wie getBackground geht hier noch nicht! setSize(400,400); show(); } public void paint (Graphics g) { g.setColor(C); g.fillRect(0,0,getSize().width-1,getSize().height-1); } public void update (Graphics g) { paint(g); } public void coloredit () // nur wenn noch kein Dialog offen ist! { if (CE==null) CE=new ColorEdit(this,this,C); } // Methoden von ColorEditListener public void setColor (Color c) { C=c; repaint(); } public void close () { CE=null; } } public class Test { public static void main (String args[]) { new TestFrame(); } }