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 werden 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 JLabel. 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 beim 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 JTextField erzeugt diese Methode kein Event, wenn die Enter-Taste gedrückt wird, sondern erzeugt dadurch einen neuen Paragraphen. Dieses Verhalten kann man aber ändern. 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.
In diesem Programm wird die TextArea nicht bearbeitbar gemacht. Alle Eingaben im TextField werden aber dort an den vorhandenen Text angehängt.
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; class TestFrame extends JFrame implements ActionListener { JTextArea textarea; JTextField textfield; 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: textarea = new JTextArea(); JScrollPane OutP = new JScrollPane(textarea); add("Center", OutP); OutP.setPreferredSize(new Dimension(400, 400)); textarea.setEditable(false); // TextField: add("South", textfield = new JTextField()); textfield.addActionListener(this); pack(); setLocationRelativeTo(null); setVisible(true); } @Override public void actionPerformed (ActionEvent e) { if (e.getSource() == textfield) // enter in TextField { textarea.append(textfield.getText() + "\n"); textfield.setText(""); } } } public class Test { public static void main (String args[]) { new TestFrame(); } }
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.BorderLayout; import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSplitPane; /** * 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)); } @Override 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(); setLocationRelativeTo(null); setVisible(true); } public static void main (String args[]) { new Test(); } }
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.
Die Klasse JComboBox ist generisch und man kann den Typ der Elemente spezifizieren. Wir verwenden hier Strings.
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; public class Test extends JFrame implements ActionListener { JLabel label; JComboBox<String> 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<String>(strings); box.addActionListener(this); setLayout(new BorderLayout()); add("Center", label); add("South", box); pack(); setLocationRelativeTo(null); setVisible(true); } @Override public void actionPerformed (ActionEvent e) { if (e.getSource() == box) { if (box.getSelectedIndex() >= -1) { label.setText(box.getSelectedItem().toString()); } } } public static void main (String args[]) { new Test(); } }
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.
Will man alle Auswahlmöglichkeiten gleichzeitig darstellen,
so gibt es JList
. Diese neue Komponente erzeugt ein Ereignis vom
Typ ListSelectionEvent
. 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.BorderLayout; import java.awt.Dimension; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JScrollPane; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; public class Test extends JFrame implements ListSelectionListener { JLabel label; JList<String> 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<String>(strings); list.addListSelectionListener(this); setLayout(new BorderLayout()); add("Center", label); add("South", new JScrollPane(list)); pack(); setLocationRelativeTo(null); setVisible(true); } @Override public void valueChanged (ListSelectionEvent e) { if (e.getSource() == list) { if (list.getSelectedIndex() >= -1) { label.setText(list.getSelectedValue().toString()); } } } public static void main (String args[]) { new Test(); } }
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 JMenuBar menu=new JMenuBar() setMenuBar(menu); // Ezeuge ein Menue JMenu files=new JMenu("Files"); menu.add(files); // Erzeuge darin einen Menue-Eintrag JMenuItem ItemClose=new JMenuItem("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 JMenuItem auch JCheckBoxItem, die ein Häkchen erhalten, wenn sie angewählt sind.
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.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; class TestFrame extends JFrame implements ActionListener { JMenuItem close; JCheckBoxMenuItem ignore; JPopupMenu popup; JButton popupclose; public TestFrame () { // build up the menu bar JMenuBar m = new JMenuBar(); setJMenuBar(m); JMenu menu = new JMenu("Test"); // first menu m.add(menu); // a normal menu item close = new JMenuItem("Close"); // an item close.addActionListener(this); menu.add(close); // a checkbox item menu.add(ignore = new JCheckBoxMenuItem("Ignore Close in Menu")); ignore.setState(true); // popup menu: popup = new JPopupMenu(); popupclose = new JButton("Close"); popup.add(popupclose); popupclose.addActionListener(this); add(popup); JPanel panel = new JPanel(); add("Center", panel); panel.addMouseListener( // for the popup menu new MouseAdapter() { @Override public void mousePressed (MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) // right mouse click { popup.pack(); popup.show(e.getComponent(), e.getX(), e.getY()); } } }); setSize(600, 600); setLocationRelativeTo(null); setVisible(true); } @Override public void actionPerformed (ActionEvent e) { if (e.getSource() == close && !ignore.getState()) { dispose(); System.exit(0); } else if (e.getSource() == popupclose) { 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. 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. Dann muss der Dialog noch mit 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.
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; class CloseQuestion extends JDialog implements ActionListener { private boolean Yes = false; public CloseQuestion (JFrame f) { super(f, "Close", true); // call constructor of dialog with modal-flag true // Label for the question: add("North", new JLabel("Really close?")); // Panel for the yes/no buttons JPanel p = new JPanel(); JButton b; p.add(b = new JButton("Yes")); b.addActionListener(this); p.add(b = new JButton("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); setVisible(true); } @Override 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; } }
Es gibt auch vorgefertigte Dialoge zur Eingabe von Text oder zur Abfrage auf Ja oder Nein. Der einfachste zeigt eine Meldung an, die der benutzer bestätigen muss. Er benötigt eine Komponente zumAnzeigen, gewöhnlich ein JFrame.
JOptionPane.showMessageDialog(this, "Error!");
Ein anderer Dialog erlaubt einzeilige Eingaben. Falls der String gleich null ist, hat der Benutzer die Eingabe abgebrochen.
String s = JOptionPane.showInputDialog(this, "Enter Name", "");
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.Color; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Panel; import java.awt.Scrollbar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.UIManager; /** * Interface, das vom Dialog benachrichtigt wird. Dies ist eine elegante Art, * zwischen Komponenten zu kommunizeiren. */ interface ColorEditListener { public void setColor (Color c); public void close (); } /** * Horizontaler Scrollbalken für eine Farbe. */ class ColorScrollbar extends JPanel implements AdjustmentListener { public int value; ColorEdit coloreditor; Scrollbar scrollbar; public ColorScrollbar (ColorEdit ce, String s, int value) { coloreditor = ce; this.value = value; // Das einfachste layout mit einer Zeile und beliebig vielen Spalten. setLayout(new GridLayout(1, 0)); // Links das Label add(new JLabel(s)); // Daneben der Scrollbalken mit Breite 40 add(scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, value, 40, 0, 255)); // Das Fenster soll die Größe 300x30 haben setPreferredSize(new Dimension(300, 30)); scrollbar.addAdjustmentListener(this); } /** * Ereignismethode für den AdjustmentListener */ @Override public void adjustmentValueChanged (AdjustmentEvent e) { value = scrollbar.getValue(); // lies neuen Wert scrollbar.setValue(value); // Scrollbar dort fixieren coloreditor.setcolor();// benachrichtige den Color Editor } public int getValue () { return value; } } /** * Dialog zum Ändern der Farbe über die Rot-, Grün-, und Blau-Komponente. */ class ColorEdit extends JDialog implements ActionListener { ColorScrollbar red, green, blue; JLabel redlabel, greenlabel, bluelabel; Color color, colorold; JPanel colorpanel; String name; ColorEditListener coloreditlistener; public ColorEdit (Frame frame, ColorEditListener cl, Color c) { super(frame, "Edit Color", false); coloreditlistener = cl; colorold = color = c; // Panel fuer die Scrollbars: JPanel center = new JPanel(); center.setLayout(new GridLayout(0, 1)); center.add(red = new ColorScrollbar(this, "Red", color.getRed())); center.add(green = new ColorScrollbar(this, "Green", color.getGreen())); center.add(blue = new ColorScrollbar(this, "Blue", color.getBlue())); add("Center", center); // Panel fuer die Buttons: Panel buttons = new Panel(); // Buttons JButton b; buttons.add(b = new JButton("OK")); b.addActionListener(this); buttons.add(b = new JButton("Cancel")); b.addActionListener(this); add("South", buttons); // Panel fuer das Farb-Display colorpanel = new JPanel(); colorpanel.add(new JLabel("Your Color")); colorpanel.setBackground(color); add("North", colorpanel); pack(); setVisible(true); } @Override public void actionPerformed (ActionEvent e) { if (e.getActionCommand().equals("Cancel")) { color = colorold; tell(); // alte Farbe zuruecksetzen } coloreditlistener.close(); dispose(); } public void setcolor () // benachrichtigt den Listener { color = new Color(red.getValue(), green.getValue(), blue.getValue()); colorpanel.setBackground(color); colorpanel.repaint(); // Zeichnet das Farb-Display neu tell(); } void tell () // Benachrichtgung an Listener { coloreditlistener.setColor(color); } } class TestFrame extends Frame implements ColorEditListener { ColorEdit CE = null; Color C; public TestFrame () { // Set an icon image, a 32x32 PNG file in the same folder as this class. setIconImage(new javax.swing.ImageIcon( getClass().getResource("hearts32.png")).getImage()); // React to the closing in another way than by setDefaultAction addWindowListener( new WindowAdapter() { @Override public void windowClosing (WindowEvent e) { dispose(); System.exit(0); } }); // Add to mouseclicks into the window. addMouseListener( new MouseAdapter() { @Override public void mousePressed (MouseEvent e) { coloredit(); } }); // Starting with the default color. C = UIManager.getColor("Panel.background"); setSize(400, 400); setLocationRelativeTo(null); setVisible(true); } @Override public void paint (Graphics g) { g.setColor(C); g.fillRect(0, 0, getSize().width - 1, getSize().height - 1); } @Override 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); CE.setLocationRelativeTo(this); } // Methoden von ColorEditListener @Override public void setColor (Color c) { C = c; repaint(); } @Override public void close () { CE = null; } } public class Test { public static void main (String args[]) { new TestFrame(); } }