Layout von Kontrollelementen | Java-Kurs | Swing Menüs |
- Ereignisse
- Buttons
- Der Button hat einen Haken
- RadioButtons
- Eingabefelder
- Fokus
- Liste (JList)
- Combobox zur Auswahl aus einer Liste
Grafische Oberflächen stellen Kontrollelemente (oft auch Widgets genannt) für wiederkehrende Aufgaben zur Verfügung. Kontrollelemente übernehmen die Kontrolle über die Maus oder die Tastatur für ihren Bereich und zeichnen sich selbst. Widgets führen zu einer applikationsübergreifenden Einheitlichkeit (Look and Feel). Swing-Kontrollelemente erweitern JComponent und sind auf ihre Aufgabe spezialisiert.
Ereignisse
Die Kontrollelemente lösen Ereignisse aus, die eine Klasse auffängt, die das Interface ActionListener implementiert. Häufig übernimmt dies die Klasse, die sich um die Darstellung kümmert, also eine Erweiterung von JFrame oder JPanel.import java.awt.event.*; public class MeinPanel extends JPanel implements ActionListener {Damit die Klasse, die ActionListener implementiert, für ein Kontrollelement aktiv wird, muss die Klasse allerdings für jedes Element mit dem Aufruf addActionListener angemeldet worden sein.
public class MeinFrame extends JFrame { // ... MeinPanel panel = new MeinPanel(); JButton click = new JButton("Drück mich!"); click.addActionListener(panel);
Das Interface ActionListener fordert von den Klassen, die es implementieren, dass sie die Methode actionPerformed implementieren. Diese Methode wird später aufgerufen, wenn ein Ereignis eintritt.
public void actionPerformed(ActionEvent ev) { if (ev.getSource()==click) { // Welcher Button? JOptionPane.showMessageDialog(null, "Sehr druckvoll!"); } }
Beim Eintreten eines Ereignisses wird die Methode actionPerformed aufgerufen. Nähere Details zum Ereignis erfährt die Methode aus dem Paramter vom Typ ActionEvent. Die Klasse stellt folgende Methoden zur Verfügung:
- Object getSource()
liefert das Objekt, dass das Ereignis auslöste - String getActionCommand()
liefert einen String, der das Ereignis beschreibt. - int getModifiers()
liefert die Kontrolltasten, die während des Ereignisses gedrückt waren - long getWhen()
liefert einen Zeitstempel - String paramString()
liefert einen Paramter als String
Buttons
Die einfachste aktive Kontrollelement ist der Button, bei Swing JButton genannt. Mit dem Aufruf von addActionListener(this) wird angemeldet, dass diese Klasse beauftragt ist, die Ereignisse des Buttons zu fangen. Dazu implementiert die Klasse das Interface ActionListener.Wer ActionListener implementiert, muss die Methode actionPerformed definieren. Sie wird von der Laufzeitumgebung aufgerufen, wenn ein Ereignis eintrifft. Der Parameter dieser Methode ist ein ActionEvent, das Informationen liefert, die zur Verarbeitung hilfreich sind, insbesondere, welcher Button eigentlich gedrückt wurde.
import javax.swing.JFrame; import javax.swing.*; import java.awt.BorderLayout; import java.awt.event.*; public class ButtonAction extends JFrame implements ActionListener { private JButton click; ButtonAction() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); click = new JButton("Drück mich!"); click.addActionListener(this); add(click); pack(); setVisible(true); } public static void main(String[] args) { new ButtonAction(); } public void actionPerformed(ActionEvent ev) { if (ev.getSource()==click) { JOptionPane.showMessageDialog(null, "Sehr druckvoll!"); } } }Das Hauptprogramm ist deutlich kleiner als der Dialog, der sich nach dem Drücken des Buttons davorschiebt.
Der Button hat einen Haken
Das Kontrollelement JCheckBox ist ein Button, der Wahr-Falsch-Entscheidungen grafisch umsetzt. Der Konstruktor übernimmt üblicherweise den Text, der die JCheckBox darstellt. Ansonsten verhält er sich weitgehend wie ein normaler Button.Auch die Ereignisse der Checkbox kann durch einen ActionListener verfolgt werden. Das Vorgehen gleicht dem beim Button. Ein Ereigns wird sowohl durch das Setzen wie auch durch das Wegnehmen des Hakens ausgelöst. Allerdings ist dieses Ereignis in der Praxis selten gefragt. Vielmehr interessiert sich das Programm für den Status der Checkbox, wenn im Dialog ein OK-Button gedrückt wurde.
Anders als der normale Button hat die JCheckBox einen Status. Sie kann selektiert sein, also einen Haken tragen oder eben nicht. Dieser Haken wird vom Benutzer gesetzt werden. Aber auch das Programm kann diesen Haken durch Aufruf der Methode setSelected setzen. Als Parameter erwartet die Methode einen booleschen Wert.
- void setSelected(boolean haken)
setzt oder löscht einen Haken, je nach Parameter haken. - boolean isSelected()
prüft, ob der Haken gesetzt ist.
import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; public class Checkbox extends JFrame implements ActionListener { private JLabel label = new JLabel("k.A."); private JCheckBox check = new JCheckBox("richtig"); public Checkbox() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLayout(new FlowLayout()); JButton click = new JButton("OK"); click.addActionListener(this); this.add(check); this.add(click); this.add(label); pack(); setVisible(true); } public static void main(String[] args) { new Checkbox(); } @Override public void actionPerformed(ActionEvent arg0) { if (check.isSelected()) { label.setText("ja"); } else { label.setText("nein"); } } }Das Programm erzeugt ein Fenster mit einer Checkbox, einem Button und einem Label. Wird der Button OK gedrückt, wird der Zustand der Checkbox ausgeleseen und das Label mit ja oder nein besetzt.
Damit mehrere Elemente in ein Fenster gesetzt werden können, muss ein Layout-Manager eingesetzt werden, hier ist es das FlowLayout.
RadioButtons
Während eine Checkbox nur zwei Zustände annehmen kann, erlauben Radiobuttons die Auswahl aus mehreren Zuständen. Radiobuttons haben ihren Namen von den Stationstasten am Radio. Es kann immer nur ein Sender gleichzeitig laufen. Egal wie viele Tasten also am Radio existieren, es kann immer nur eine gedrückt sein.
Radiobuttons müssen gruppiert werden
Die einzelnen Buttons sind Objekte der Klasse JRadiobutton.
JRadioButton radioMaennlich = new JRadioButton("männlich"); JRadioButton radioWeiblich = new JRadioButton("weiblich");Sie können nun in ein Fenster eingesetzt und angeklickt werden. Allerdings kann man derzeit noch sowohl männlich als auch weiblich sein. Um die typische Radio-Funktion zu erreichen, müssen die Buttons einer ButtonGroup zugeordnet werden.
ButtonGroup grpGeschlecht = new ButtonGroup(); grpGeschlecht.add(radioMaennlich); grpGeschlecht.add(radioWeiblich); radioMaennlich.setSelected(true);
Ereignisse und auslesen
Auch Radiobuttons können Ereignisse auslösen, die durch einen ActionListener empfangen werden, sofern dieser angemeldet wurde. In der Regel ist das wie bei der Checkbox nicht erforderlich.Wie bei der Checkbox kann der Zustand jedes Radiobuttons mit isSelected ausgelesen oder mit setSelected gesetzt werden.
Beispiel
Das folgende Beispielprogramm zeigt Radiobuttons für vier Farben. Wird auf den OK-Button geklickt, ändert dieser die Farbe, je nachdem, welcher Radiobutton angewählt ist. Klickt man auf einen der Radiobuttons, erscheint im Label die aktuell angewählte Farbe:import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JRadioButton; public class RadioButton extends JFrame implements ActionListener { private JLabel labakt = new JLabel("aktuell"); private JButton bt = new JButton("OK"); private String[] strFarbe = { "gelb", "rot", "grün", "blau" }; private Color[] col = { Color.yellow, Color.red, Color.green, Color.blue }; private JRadioButton[] radbt = new JRadioButton[strFarbe.length]; private ButtonGroup radgrp = new ButtonGroup(); public RadioButton() { this.setLayout(new GridLayout(2, 4)); for (int i = 0; i < strFarbe.length; i++) { radbt[i] = new JRadioButton(strFarbe[i]); radgrp.add(radbt[i]); // zur Gruppe hinzufügen radbt[i].setBackground(col[i]); // bunt machen radbt[i].addActionListener(this); this.add(radbt[i]); // auf das Panel setzen } radbt[0].setSelected(true); // Auswahl vorgeben // Setze sie auf das Panel this.add(bt); bt.addActionListener(this); this.add(new JLabel(" ")); // Auffüllen this.add(labakt); pack(); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { new RadioButton(); } private int getRadIndex() { for (int i = 0; i < strFarbe.length; i++) { if (radbt[i].isSelected()) { return i; } } return 0; } @Override public void actionPerformed(ActionEvent evt) { int i = getRadIndex(); if (evt.getSource() == bt) { bt.setBackground(col[i]); } else { labakt.setText(strFarbe[i]); } } }
Für die Anordnung der Kontrollelemente wurde als Layout-Manager dieses Mal das GridLayout verwendet. In einem Raster werden alle Elemente gleich groß dargestellt. Die Elemente werden auf den verfügbaren Platz aufgeblasen.
Eingabefelder
Für die Eingabe von Daten gibt es das Kontrollelement JTextField. Der Benutzer kann dort eine Zeile eintippen. Die Eingabe wird mit der Methode getText ausgelesen. Das Ergebnis steht dann in einer Variable vom Typ String. Bei Zahleneingaben muss der String in eine Zahlenvariable umgewandelt werden.JTextField eingabeFeld = new JTextField();Die wichtigsten Aufgaben sind das Auslesen einer Benutzereingabe mit getText. Hin und wieder möchte man einen Wert vorgeben. Dann wird die Methode setText verwendet.
eingabeFeld.setText("1.4142135"); String inhalt = eingabeFeld.getText();
Sonderfälle Passwort und mehrzeiliger Text
Abwandlungen sind das JPasswordField, mit dem man eben Passwörter eingeben lassen kann. Die Eingabe wird dann verdeckt.Das JTextArea stellt ein mehrzeiliges Eingabefeld dar. Damit kann leicht ein kleiner Editor in die Anwendung eingebaut werden. Wird der Inhalt mit setEditable(false) als nicht editierbar gesetzt, kann man damit leicht längere Passagen wie Copyrightvermerke anzeigen.
- void setEditable(boolean)
boolean isEditable()
Setzt und prüft die Editierbarkeit des Eingabefelds. - void setHorizontalAlignment(int)
int getHorizontalAlignment()
Setzt und prüft die horizontale Ausrichtung. Als Parameter werden die Konstanten JTextField.LEADING für linksbündig, JTextField.CENTER für zentriert oder JTextField.TRAILING für rechtsbündig verwendet.
Fokus
Wenn ein Kontrollelement die Tastenkontrolle hat, hat es den Fokus. Verlässt der Anwender das Kontrollelement, so verliert es den Fokus.Bei Eingabefeldern ist das Ereignis, dass es den Fokus verliert von hohem Interesse, wenn man beispielsweise die Eingabe prüfen will oder Zwischenergebnisse berechnen will. Dazu muss ein FocusListener aufgesetzt werden.
Das folgende Beispiel ist ein Programm mit zwei Eingabefeldern, deren Fokus beobachtet wird. Zwischen den beiden Eingabefeldern wird ein TextArea-Feld benutzt, um die Ereignisse anzuzeigen. Bei der Gelegenheit wird noch gezeigt, wie das JTextArea-Element in ein JScrollPane gesteckt wird, das dann die Verwaltung der Schiebebalken übernimmt, wenn der Textinhalt größer wird als der Programmausschnitt anzeigt.
import javax.swing.*; import java.awt.BorderLayout; import java.awt.event.*; public class TestFocus extends JFrame implements FocusListener { JTextArea anzeige; JTextField txtOben, txtUnten; TestFocus() { setDefaultCloseOperation(EXIT_ON_CLOSE); // Schließen bewirkt Ende setLayout(new BorderLayout()); // Layout auf Border festlegen anzeige = new JTextArea(); // Multi-Line-Edit als Anzeige anzeige.setEditable(false); JScrollPane scrollRahmen = new JScrollPane(anzeige); // Schiebebalken um Anzeige txtOben = new JTextField(); // Eingabefeld anlegen txtOben.addFocusListener(this); // FocusListener anmelden txtUnten = new JTextField(); txtUnten.addFocusListener(this); add(scrollRahmen, BorderLayout.CENTER); // Elemente ins Layout bringen add(txtOben, BorderLayout.NORTH); add(txtUnten, BorderLayout.SOUTH); setSize(400, 300); // Rahmen groß machen setVisible(true); // Rahmen sichtbar machen } @Override public void focusGained(FocusEvent e) { // Focus erhalten if (e.getSource() == txtOben) { // wer hat das Ereignis ausgelöst? anzeige.append("Focus erhalten: txtOben\n"); } else if (e.getSource() == txtUnten) { anzeige.append("Focus erhalten: txtUnten\n"); } } @Override public void focusLost(FocusEvent e) { // Focus verloren if (e.getSource() == txtOben) { // wer hat das Ereignis ausgelöst? anzeige.append("Focus verloren: txtOben\n"); } else if (e.getSource() == txtUnten) { anzeige.append("Focus verloren: txtUnten\n"); } } public static void main(String[] args) { new TestFocus(); // gib Gas! } }
Das Testprgramm verwendet für die Anordnung der Textfelder ein BorderLayout als Layout-Manager. Dadurch wird der verfügbare Platz an das Element in der Mitte vergeben, während die Ränder nur das bekommen, was sie brauchen.
Interessant ist auch, dass der Fokus verloren geht, wenn ein fremdes Fenster in den Vorgrund kommt.
Liste
Die JList ist ein Kontrollelement, das Listen darstellen kann. In seiner einfachsten Art bestehen diese Listen aus Strings.import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JScrollPane; public class Liste extends JFrame { public static void main(String[] args) { new Liste(); } public Liste() { String[] inhalt = {"Tomatensuppe", "Kartoffelschnee", "Gulaschsuppe", "Puree de pommes", "Erbsensuppe"}; JList<String> liste = new JList<>(inhalt); JScrollPane scroll = new JScrollPane(liste); add(scroll); setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); // Gegenstück zu setSize setVisible(true); // sichtbar machen } }Die JList stellt ein Array von Strings dar, das im Konstruktor übergeben wird. Eine JList wird typischerweise über Generics auf String festgelegt. Die JScrollPane bildet einen Container für die JList. Sie erzeugt einen Schiebebalken, sobald der Listeninhalt nicht mehr vollständig darstellbar ist.
Listenänderungen am Modell
Wenn sich der Inhalt der Liste im Verlauf des Programms ändern soll, kommen wir mit dem String-Array nicht weiter. Denn nachträgliche Änderungen am String-Array schlagen nicht auf die JList durch, da das Array nicht weiß, dass es von der Liste verwendet wird.In diese Lücke springt das Listenmodell. Es kann auch mit einem String-Array initialisiert werden. Das Modell wird dann der JList als Konstruktorparameter übergeben.
import javax.swing.DefaultListModel; import javax.swing.JList; import javax.swing.JScrollPane; // ... DefaultListModel<String> model = new DefaultListModel<>(); JList<String> liste = new JList<>(inhalt); JScrollPane scroll = new JScrollPane(liste);Um den Inhalt der Liste zu ändern, werden diese am Modell ausgeführt. Dazu stellt DefaultListModel folgende Methoden zur Verfügung.
- Die Strings werden mit addElement Schritt für Schritt gefüllt.
- Mit der Methode addAll können Listen wie ArrayLists angehängt werden.
- Mit dem Aufruf von removeElement werden Elemente entfernt.
- Durch removeAllElements werden alle Elemente entfernt.
Das folgende Beispiel zeigt, wie das String-Array aus dem ersten Beispiel über eine Schleife in das Modell übernommen werden kann.
String[] inhalt = {"Tomatensuppe", "Kartoffelschnee", "Gulaschsuppe", "Puree de pommes de terre", "Erbsensuppe"}; DefaultListModel<String> model = new DefaultListModel<>(); for (String element : inhalt) { model.addElement(element); } JList<String> liste = new JList<String>(model);
Mit der Methode addAll kann die Liste mit einem Schlag übernommen werden. Allerdings muss dazu das String-Array mit der Funktion Arrays.asList zunächst in eine ArrayList verwandelt werden.
List<String> arraylist = new ArrayList<String>(); arraylist.addAll(Arrays.asList(inhalt)); model.addAll(arraylist);
Etwas flotter geht das in einer Zeile:
model.addAll(Arrays.asList(inhalt));
Listen selektieren und auslesen
Die JList kann mehrere Listenelemente selektieren, in dem der Benutzer bei gedrückter [Strg]-Taste klickt. Entsprechend kann mit getSelectedIndex die Nummer des selektierten Elements oder mit getSelectedIndices ein Integer-Array aller selektierten Elemente ermittelt werden. Das Programm kann mit setSelectedIndex bzw. setSelectedIndices Elemente selektieren.Um den Inhalt des Listenelements zu bekommen, muss man sich an das Modell wenden und die Methode elementAt aufrufen.
liste.setSelectedIndex(nr); int index = liste.getSelectedIndex(); String str = model.elementAt(index);
Ereignisse
In manchen Fällen will man kontrollieren, ob ein Wechsel der Selektion stattgefunden hat. Dazu wird ein ListSelectionListener erzeugt, der die Methode valueChanged aufruft, sobald ein Listenelement angeklickt wird.liste.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent arg0) { // TODO Auto-generated method stub } });
Ein Beispiel für den Umgang mit einer JList finden Sie im Projekt CodeKnacker.
Combobox zur Auswahl aus einer Liste
Eine Combobox könnte man auch als Klappliste bezeichnen. Der ausgewählte Eintrag der Liste ist offen sichtbar, die anderen Optionen klappen heraus, wenn man den Button des Elements anklickt.Die Combobox eignet sich als Ersatz für Radiobuttons oder als Ersatz für eine Liste, die nur eine Auswahl zulässt.
String[] fuellung = {"Tomatensuppe", "Kartoffelschnee", "Gulaschsuppe", "Puree de pommes de terre", "Erbsensuppe"}; waehler = new JComboBoxEine JComboBox verhält sich ähnlich wie eine JList. Das angewählte Element ist sichtbar. Duch Anklicken fährt die Liste aus. Besonderheit: Die sichtbare Zeile kann wie ein JTextField editierbar gemacht werden.(fuellung); waehler.setEditable(true);
Die Events einer JComboBox werden durch einen ActionListener gefangen. In der Methode actionPerformed wird ermittelt, ob das Feld editiert wurde.
@Override public void actionPerformed(ActionEvent e) { if (e.getSource()==waehler) { if (e.getActionCommand().equals("comboBoxEdited")) { // Combobox wurde editiert String str = (String) waehler.getSelectedItem(); if (str!=null && !str.isEmpty()) { waehler.addItem(str); } } else { index = waehler.getSelectedIndex(); if (index>=0) { label.setText(waehler.getItemAt(index)); } } } }Bei der Auswertung wird einfach abgefragt, welches Feld ausgewählt war. Der Rückgabewert ist der Index der Auswahl, beginnend bei 0.
int index = wahl.getSelectedIndex();Man kann auch der Combobox einen ActionListener zuweisen. Dann kann das Programm auf jede Änderung in der Box reagieren.
wahl.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JComboBox jcmbType = (JComboBox) e.getSource(); String cmbType = (String) jcmbType.getSelectedItem(); label.setIcon(new ImageIcon("" + cmbType.trim()) + ".jpg")); } });Die Methoden von JComboBox:
- getSelectedItem()
gibt String zurück - getSelectedIndex()
gibt die Nummer zurück - setEditable(true)
erlaubt das Editieren der Einträge - setAlignmentX(Component.LEFT_ALIGNMENT)
waagerechte Ausrichtung der Einträge
Ein naher Verwandter der Combobox ist die JList. Das ist eine Liste, die nicht ausklappt, sondern einen größeren Raum für alle Zeilen einnimmt.
Strengere Typisierung
In den neueren Java-Versionen wird es immer eine Warnung geben, wenn man JComboBox ohne Typisierung benutzt. Der Hintergrund ist, dass die Schnittstellen Object verwenden.JSpinner
Ein Spinner ist vergleichbar mit einer Combobox, mit dem Unterschied, dass sie nicht aufklappt, sondern der Anwender mit zwei Pfeilelementen aus den Vorgaben auswählt.
JSpinner spinner = new JSpinner();Wenn nichts angegeben wird, enthält der Spinner ganze Zahlen, die bei 0 beginnen. Um die Werte zu konfigurieren, können Modelle erstellt werden.
Zahlenspinner
Mit dem SpinnerNumberModel können die Zahlen bestimmt werden, die der Spinner annehmen kann. Dazu übergeben Sie dem Konstruktor vier Werte: der zunächst ausgewählte Startwert, der minimale und der maximale Wert, den der Spinner annehmen soll und die Schrittweite des Spinners.
double start = 7.5, min = 5, max = 9.9, step = .5; SpinnerNumberModel spinmod = new SpinnerNumberModel(start, min, max, step); JSpinner spinner = new JSpinner(spinmod);
Der eingestellte Wert wird über das Modell mit der Methode getValue gelesen. Dabei wird das Objekt einer Wrapperklasse zurückgegeben, die entsprechend gecastet werden muss.
Double zahl = (Double) spinmod.getValue();
Stringspinner
Anstelle von Zahlen können auch Texte verwendet werden, zwischen denen gewählt wird.
String[] strar = {"Anton", "Berta", "Caesar","Dora"}; SpinnerListModel spinmod = new SpinnerListModel(strar); JSpinner spinner = new JSpinner(spinmod);
Auch bei dem SpinnerListModel wird die Methode getValue verwendet, um den eingestellten Wert zu liefern. Hier allerdings ist der Rückgabetyp ein String.
String text = (String) spinmod.getValue();
Datumspinner
Sie können den Spinner auch zur Datumsauswahl verwenden. Dazu wird ein SpinnerDateModel verwendet.
SpinnerDateModel spinmod = new SpinnerDateModel(); JSpinner spinner = new JSpinner(spinmod);Ähnlich wie beim Zahlenmodell kann dem Konstruktor auch hier per Parameter übergeben werden, wie gestartet wird, welche Grenzen und Schrittweiten erlaubt sind.
Das folgende Programm enthält einen Datumsspinner und einen Button. Sobald auf den Button geklickt wird, wird das Datum des Spinners ausgelesen und hier der Einfachheit halber auf der Konsole ausgegeben werden.
import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JSpinner; import javax.swing.SpinnerDateModel; public class Spinner extends JFrame implements ActionListener { SpinnerDateModel spinmod = new SpinnerDateModel(); JSpinner spinner; public Spinner() { this.setLayout(new FlowLayout()); spinner = new JSpinner(spinmod); this.add(spinner); JButton button = new JButton("Klick"); button.addActionListener(this); this.add(button); setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); // Gegenstück zu setSize setVisible(true); // sichtbar machen } public static void main(String[] args) { new Spinner(); } @Override public void actionPerformed(ActionEvent e) { Date date = spinmod.getDate(); System.out.println(date); } }
Das Programm sieht wie folgt aus und gibt auf der Konsole den eingestellten Wert aus.
Ein Beispiel für den Umgang mit einem JSpinner finden Sie im Projekt CodeKnacker.
Layout von Kontrollelementen | Swing Menüs |